Quality control
Data import
Load raw data. The main table contains already normalized quantification of all sgRNAs, fold change, multiple hypothesis corrected p-values, and fitness score. Contrary to the processing of our first CRISPRi library V1, much of the functionality from the notebook was transferred into the new CRISPRi library pipeline on github.
# load first seq run
load("../data/input/DESeq2_result.Rdata")
df_main <- DESeq_result_table
# load second seq run
load("../data/input/DESeq2_result_2.Rdata")
df_main <- bind_rows(df_main, DESeq_result_table)
# remove single results table
rm(DESeq_result_table)
Data annotation
Different annotation columns are added to the main data frame, including a short sgRNA identifier (excluding the position on the gene), an sgRNA index (1 to 5), and genome annotation from Uniprot. The Uniprot data is dynamically downloaded for every update of this pipeline using their very simple API (read_tsv("https://www.uniprot.org/uniprot/?query=taxonomy:1111708&format=tab")). The full list of columns that can be queried is available here. Pathway annotation from KEGG is later in the pipeline added using the KEGGREST package.
df_main <- df_main %>%
# correct an error in sgRNA naming
mutate(sgRNA = gsub('”', '2', sgRNA)) %>%
# split sgRNA names into target gene and position
separate(sgRNA, into = c("sgRNA_target", "sgRNA_position"), sep = "\\|",
remove = FALSE) %>%
# add sgRNA index number (1 to maximally 5) and type
group_by(sgRNA_target) %>%
mutate(
sgRNA_position = as.numeric(sgRNA_position),
sgRNA_index = sgRNA_position %>% as.factor %>% as.numeric,
sgRNA_type = if_else(grepl("^nc_", sgRNA), "ncRNA", "gene")) %>%
ungroup %>%
# map trivial names to LocusTags using a manually curated list
left_join(
read_tsv("../data/input/mapping_trivial_names.tsv", col_types = cols()),
by = c("sgRNA_target" = "gene")) %>%
# remove some empty rows (NA targets)
filter(!is.na(sgRNA_target)) %>%
# remove 2 conditions without response
filter(!condition %in% c("BG11", "LC, 200uE")) %>%
# split condition into separate cols
separate(condition, into = c("carbon", "light", "treatment_1", "treatment_2"),
sep = ", ", remove = FALSE, fill = "right") %>%
unite("treatment", treatment_1, treatment_2, sep = ", ", na.rm = TRUE)
Overview about the different conditions.
df_cultivation_summary <- df_main %>% group_by(condition) %>%
summarize(
time_points = paste(unique(time), collapse = ", "),
carbon = unique(carbon),
light = unique(light),
treatment = unique(treatment),
min_fit = min(fitness),
med_fit = median(fitness),
max_fit = max(fitness))
print(df_cultivation_summary)
write_csv(df_cultivation_summary, file = "../data/output/cultivation_summary.csv")
Retrieve gene info from uniprot and merge with main data frame. We need to make a custom function to retrieve and parse the data from uniprot, because of a bug in the security level on Ubuntu 20.04. The fallback option is to load a local copy of uniprot annotation for this organism.
library(httr)
uniprot_url <- paste0(
"https://www.uniprot.org/uniprot/?query=taxonomy:1111708&format=tab&",
"columns=id,genes,genes(PREFERRED),protein_names,length,mass,ec,database(KEGG)")
get_uniprot <- function(url) {
# reset security level, caused by a faulty SSL certificate on server side,
# see this thread: https://github.com/Ensembl/ensembl-rest/issues/427
httr_config <- config(ssl_cipher_list = "DEFAULT@SECLEVEL=1")
res <- with_config(config = httr_config, GET(url))
server_error = simpleError("")
df_uniprot <- tryCatch(
read_tsv(content(res), col_types = cols()),
error = function(server_error) {
message("Uniprot server not available, falling back on local Uniprot DB copy")
read_tsv("../data/input/uniprot_synechocystis.tsv", col_types = cols())
}
)
}
df_uniprot <- get_uniprot(uniprot_url) %>%
rename_with(tolower) %>%
rename(locus = `cross-reference (kegg)`, gene_name = `gene names`,
gene_name_short = `gene names (primary )`, ec_number = `ec number`,
protein = `protein names`, uniprot_ID = entry
) %>%
separate_rows(locus, sep = ";syn:") %>%
mutate(locus = str_remove_all(locus, "syn:|;")) %>%
filter(!is.na(locus))
df_main <- left_join(df_main, filter(df_uniprot, !duplicated(locus)),
by = "locus")
Number of sgRNAs
Each gene is represented by up to five sgRNAs. We can test if all or only some of the 5 sgRNAs are “behaving” in the same way in the same conditions, more mathematically speaking we can estimate the correlation of every sgRNA with another. First let’s summarize how many genes have 5, 4, 3 sgRNAs and so on associated with them.
# N unique sgRNAs in dataset
paste0("Number of unique sgRNAs: ", unique(df_main$sgRNA) %>% length)
[1] "Number of unique sgRNAs: 21705"
# N genes with 1,2,3,4 or 5 sgRNAs
plot_sgRNAs_per_gene <- df_main %>%
group_by(sgRNA_type, sgRNA_target) %>%
summarize(n_sgRNAs = length(unique(sgRNA_position)), .groups = "drop_last") %>%
count(n_sgRNAs) %>% filter(n_sgRNAs <= 5) %>%
ggplot(aes(x = factor(n_sgRNAs, 5:1), y = n, label = n)) +
geom_col(show.legend = FALSE) +
geom_text(size = 3, nudge_y = 200, color = grey(0.5)) +
facet_grid(~ sgRNA_type) +
labs(x = "n sgRNAs / target", y = "n targets") +
coord_cartesian(ylim = c(-50, 3500)) +
custom_theme()
print(plot_sgRNAs_per_gene)

save_plot(plot_sgRNAs_per_gene, width = 6, height = 3.5)
Normalization
Fitness distribution of all conditions
Before biological analysis continues, we need to check if fitness (and log2 FC from which it is calculated) is equally distributed. For example, strictly essential genes like ribosomal genes should show the same degreee of depletion over time, regardless of condition.
We can compare fitness over all conditions using a scatter plot matrix. We can see that some conditions are very similar to each other, for example the conditions treated with glucose (LC, LL +g, LC, LL, +D, +G, HC, LL +g). Others are more dissimilar to the rest, for example LC, IL and LC, LL, +FL. They are more alike each other, although LC, LL, +FL should be more comparable to LC, LL, hinting at experimental bias. In this case both of these conditions (and LC, LL, +G) were pre-cultivated in low light instead of high light, as opposed to the rest of the samples.
df_main %>% filter(time == 0, sgRNA_index == 1) %>%
select(locus, condition, fitness) %>%
filter(!is.na(locus)) %>%
pivot_wider(names_from = condition, values_from = fitness) %>%
select(-locus) %>%
custom_splom(pch = 19, cex = 0.3, col = grey(0.4, 0.4), pscales = 0)

Normalization strategy
In order to account for experimental or quantification bias, we can try to normalize the log2 FC distribution between all samples, and then re-calculate fitness. The underlying assumption is that e.g. essential genes should deplete at the same rate and hence show identical log2 FC at identical time points. Different types of experimental bias influence global fitness distribution and should be reduced with normalization. Here we try a ‘cyclic loess’ or quantile normalization that gave good results in a quick comparison.
# construct a normalization function that takes three colums as input,
# the numeric variable to be normalized, the conditioning variable
# (character or factor), and an ID that identifies each observation (sgRNA)
apply_norm = function(id, cond, var) {
df_orig <- tibble(id = id, cond = cond, var = var)
df_new <- pivot_wider(df_orig, names_from = cond, values_from = var) %>%
column_to_rownames("id") %>% as.matrix %>%
limma::normalizeBetweenArrays(method = "quantile") %>%
as_tibble(rownames = "id") %>%
pivot_longer(-id, names_to = "cond", values_to = "var_norm")
left_join(df_orig, df_new, by = c("id", "cond")) %>% pull(var_norm)
}
# apply normalization
df_main <- df_main %>%
mutate(FoldChange = 2^log2FoldChange) %>%
group_by(time) %>%
mutate(
FoldChange_norm = apply_norm(sgRNA, condition, FoldChange),
log2FoldChange = log2(FoldChange_norm)
) %>% ungroup
# compare effect of normalization
df_main %>% group_by(condition) %>% slice(1:10000) %>%
ggplot(aes(x = log2(FoldChange), y = log2(FoldChange_norm), color = factor(time))) +
geom_point(size = 0.5) +
facet_wrap(~ condition, ncol = 4) +
custom_theme() +
scale_color_manual(values = custom_colors)

Another way to look at the result of the normalization is to compare the global distribution of log2 FC values, as a density plot.
library(ggridges)
df_main %>% filter(time == 10) %>%
select(sgRNA, condition, FoldChange, FoldChange_norm) %>%
pivot_longer(matches("^Fold"), names_to = "metric", values_to = "FC") %>%
distinct %>%
ggplot(aes(x = log2(FC), y = condition, group = condition)) +
geom_density_ridges(fill = "#00AFBB99", col = grey(0.4)) +
facet_wrap(~ metric, ncol = 4) +
lims(x = c(-2, 1.5)) +
custom_theme()
Picking joint bandwidth of 0.0306
Picking joint bandwidth of 0.0312

Now we need to re-calculate fitness based on the normalized log2 FC.
df_main <- df_main %>%
select(-FoldChange, -FoldChange_norm) %>%
group_by(sgRNA, condition) %>%
mutate(fitness = DescTools::AUC(time, log2FoldChange)/(max(time)/2)) %>%
arrange(sgRNA_target, sgRNA_index, condition, time)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Fitness score aggregation
Correlation of sgRNAs
Different methods can be used to estimate similarity between samples (sgRNAs). For example, factor analysis is a method to dissect underlying sources of variation within the dataset, and the contribution to overall variation. The most famous example is principal component analysis (PCA). We can also use the correlation coefficient of sgRNAs to each other to see if one of the sgRNAs contributes stronger to overall variation.
This is an example of an apparently strictly essential gene, encoding the ribosomal protein rps10. Most of the sgRNA repressor strains are depleted, the correlation between sgRNAs is high. The strength of depletion varies though, and the strain with sgRNA 3 is not depleted at all. We want to give higher weights to sgRNAs that correlate well with each other, and/or show stronger effect (depletion/enrichment).
plot_sgRNA_ribo_example <- df_main %>% filter(sgRNA_target == "rps10") %>%
mutate(sgRNA_index = factor(sgRNA_index, 1:5)) %>%
ggplot(aes(x = time, y = log2FoldChange, color = sgRNA_index)) +
geom_line(size = 1) + geom_point(size = 2) +
facet_wrap(~ condition, ncol = 4) +
custom_theme() +
scale_color_manual(values = custom_range(5))
print(plot_sgRNA_ribo_example)

save_plot(plot_sgRNA_ribo_example, width = 7, height = 5.5)
A correlation score can be calculated by computing the correlation coefficient of all sgRNAs to each other. This score is robustly summarized by taking the median, and rescaling it from the respective minima and maxima [-1, 1] to [0, 1]. This score serves as a weight component for each sgRNA to calculate the (global) weighted mean of log2 FC over all sgRNAs. The score has the characteristic that it gives a weight of 1 for an sgRNA perfectly correlated with all other sgRNAs of the same gene, and a weight of 0 for sgRNAs perfectly anti-correlated to the other sgRNAs.
For a matrix of \(x = 1 .. m\) sgRNAs and \(y = 1 .. n\) observations (measurements), the correlation \(R\) of one sgRNA to another is calculated using Pearson’s method:
\(R_x=cor([log_2FC_{x1,y1} ... log_2FC_{x1,yn}], [log_2FC_{x2,y1} ... log_2FC_{x2,yn}])\)
The correlation weight of one sgRNA is then calculated as median of all \(R\) rescaled between 0 and 1.
\(w_x = \frac{1 + median(R_1, R_2, ..., R_m)}{2}\)
The following example shows the correlation matrix for the 5 rps10 sgRNAs, and their weights. The self correlation of each sgRNA (R = 1) is removed prior to weight determination.
cor_matrix <- df_main %>% filter(sgRNA_target == "rps10") %>% ungroup %>%
select(sgRNA_index, log2FoldChange, condition, time) %>%
pivot_wider(names_from = c("condition", "time"), values_from = log2FoldChange) %>%
arrange(sgRNA_index) %>% column_to_rownames("sgRNA_index") %>%
as.matrix %>% t %>% cor(method = "pearson")
weights <- cor_matrix %>% replace(., . == 1, NA) %>%
apply(2, function(x) median(x, na.rm = TRUE)) %>%
rescale(from = c(-1, 1), to = c(0, 1))
# plot heatmap
lattice::levelplot(cor_matrix %>% replace(., . == 1, NA),
col.regions = custom_range(20))

# print weights
weights
1 2 3 4 5
0.8440521 0.7864564 0.4605635 0.8265134 0.7689177
Now we can create a function that will compute weights for all sgRNAs, and add the weights to the data set.
determine_corr <- function(index, value, condition, time) {
# make correlation matrix
df <- data.frame(index = index, value = value, condition = condition, time = time)
cor_matrix <- pivot_wider(df, names_from = c("condition", "time"), values_from = value) %>%
arrange(index) %>% column_to_rownames("index") %>%
as.matrix %>% t %>% cor(method = "pearson")
# determine weights
weights <- cor_matrix %>% replace(., . == 1, NA) %>%
apply(2, function(x) median(x, na.rm = TRUE)) %>%
scales::rescale(from = c(-1, 1), to = c(0, 1)) %>%
enframe("index", "weight") %>% mutate(index = as.numeric(index)) %>%
mutate(weight = replace(weight, is.na(weight), 1))
# return vector of weights the same order and length
# as sgRNA index vector
left_join(df, weights, by = "index") %>% pull(weight)
}
df_main <- df_main %>%
group_by(sgRNA_target) %>%
mutate(sgRNA_correlation = determine_corr(sgRNA_index,
log2FoldChange, condition, time))
Efficiency of sgRNAs
The correlation of each sgRNA with each other is a “global” parameter as it is identical over all conditions. A second global parameter, sgRNA efficiency, can be obtained using a similar approach. We expect that fitness of all sgRNAs for one gene is not normally distributed because sgRNAs are not ideal replicate measurements. They are biased by position effects and off-target binding, see Wang et al., Nature Comms, 2018 for a very insightful and comprehensive analysis of the number and position of sgRNAs required to estimate gene fitness.
We calculate sgRNA efficiency \(E\) as the median absolute fitness (AUC of log2FC over time) of an sgRNA \(x = 1 .. m\) over all observations [conditions] \(y = 1 .. n\).
\(E_x=median(abs(fitness_{x1, y1}, fitness_{x1, y2}, ..., fitness_{x1, yn}))\)
To normalize between all sgRNAs, \(E\) is rescaled to a range between 0 and 1.
\(E_x=\frac{E_x}{max(E_1, E_2, ..., E_m)}\)
df_main <- df_main %>% group_by(sgRNA_target) %>%
mutate(sgRNA_efficiency = ave(fitness, sgRNA_index, FUN = function(x) median(abs(x))) %>%
{./max(.)})
This is the resulting sgRNA efficiency for the example gene above, rps10.
df_main %>% filter(sgRNA_target == "rps10") %>% ungroup %>%
select(sgRNA_index, sgRNA_efficiency) %>% distinct %>%
arrange(sgRNA_index) %>% deframe
1 2 3 4 5
1.0000000 0.1519365 0.0351794 0.2105323 0.5110918
Position bias of sgRNA repression
Plot the weight of each sgRNA to see if there is a dependency between correlation and sgRNA position. There is no significant trend.
We can also quantify how many genes have strongly correlated sgRNAs and how many have outliers. In order to do this, the median weight of the (up to) 5 sgRNAs per gene is plotted. Generally, the median weight ranges between 0.5 and 1.0, showing on average good correlation.
plot_sgRNA_correlation <- df_main %>%
select(sgRNA_target, sgRNA_index, sgRNA_correlation) %>%
filter(sgRNA_index <= 5) %>%
distinct %>%
# plot
ggplot(aes(x = factor(sgRNA_index), y = sgRNA_correlation)) +
geom_boxplot(outlier.shape = "") +
labs(x = "sgRNA position", y = "correlation") +
stat_summary(fun.data = function(x) c(y = median(x)+0.07,
label = round(median(x), 2)), geom = "text", size = 3) +
stat_summary(fun.data = function(x) c(y = 1.1,
label = length(x)), geom = "text", color = grey(0.5), size = 3) +
coord_cartesian(ylim = c(-0.15, 1.15)) +
custom_theme()
plot_sgRNA_correlation_hist <- df_main %>%
select(sgRNA_target, sgRNA_index, sgRNA_correlation) %>%
filter(sgRNA_index <= 5) %>%
distinct %>% group_by(sgRNA_target) %>%
summarize(
median_sgRNA_correlation = median(sgRNA_correlation),
min_sgRNA_correlation = min(sgRNA_correlation)
) %>%
# plot
ggplot(aes(x = median_sgRNA_correlation)) +
geom_histogram(bins = 40, fill = custom_colors[1], alpha = 0.7) +
custom_theme()
save_plot(plot_sgRNA_correlation_hist, width = 5, height = 4)
save_plot(plot_sgRNA_correlation, width = 5, height = 4)
ggarrange(plot_sgRNA_correlation, plot_sgRNA_correlation_hist, ncol = 2)

Second, the binding position of the sgRNAs could be correlated to the strength of repression. In other words sgRNAs binding closer to the promoter could have stronger ability to repress a gene, see Figure 1 B in Wang et al., Nature Comms, 2018. We plot sgRNA efficiency for genes only, because the absolute majority of those has 5 sgRNAs.
plot_sgRNA_efficiency <- df_main %>%
filter(sgRNA_index <= 5, sgRNA_type == "gene") %>%
select(sgRNA_target, sgRNA_index, sgRNA_efficiency) %>% distinct %>%
ggplot(aes(x = factor(sgRNA_index), y = sgRNA_efficiency)) +
geom_boxplot(notch = FALSE, outlier.shape = ".") +
labs(x = "sgRNA position (relative)", y = "repression efficiency") +
coord_cartesian(ylim = c(-0.15, 1.15)) +
stat_summary(fun.data = function(x) c(y = median(x)+0.07,
label = round(median(x), 2)), geom = "text", size = 3) +
stat_summary(fun.data = function(x) c(y = 1.1,
label = length(x)), geom = "text", color = grey(0.5), size = 3) +
custom_theme()
plot_sgRNA_efficiency_hist <- df_main %>%
filter(sgRNA_index <= 5, sgRNA_type == "gene") %>%
select(sgRNA_target, sgRNA_position, sgRNA_efficiency) %>% distinct %>%
group_by(sgRNA_position) %>%
summarize(sgRNA_efficiency = median(sgRNA_efficiency), n_pos = n()) %>%
filter(n_pos >= 10) %>%
ggplot(aes(x = sgRNA_position, y = sgRNA_efficiency)) +
labs(x = "sgRNA position (nt)", y = "repression efficiency") +
geom_point(col = alpha(custom_colors[5], 0.5)) +
geom_smooth() +
custom_theme()
save_plot(plot_sgRNA_efficiency, width = 5, height = 4)
save_plot(plot_sgRNA_efficiency_hist, width = 5, height = 4)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
ggarrange(plot_sgRNA_efficiency, plot_sgRNA_efficiency_hist, ncol = 2)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Export draft Figure 1 for manuscript.
plot_selected_sgRNAs <- df_main %>%
filter(
grepl("ctrl[1-5]$|rps10$", sgRNA_target),
condition %in% c("HC, HL", "HC, LL", "LC, IL", "LC, LL")) %>%
mutate(
sgRNA_index2 = as.numeric(str_extract(sgRNA_target, "[1-9]$")),
sgRNA_index = case_when(sgRNA_position == 0 ~ sgRNA_index2, TRUE ~ sgRNA_index),
sgRNA_target = str_extract(sgRNA_target, "[a-zA-Z]*")
) %>%
ggplot(aes(x = time, y = log2FoldChange, color = factor(sgRNA_index))) +
geom_line(size = 1) + geom_point(size = 2) +
facet_grid(sgRNA_target ~ condition) +
custom_theme(legend.position = 0) +
coord_cartesian(ylim = c(-4.5, 2.5)) +
scale_color_manual(values = custom_range(5))
svg(filename = "../figures/figure1.svg", width = 7, height = 5.5)
ggarrange(ncol = 2, nrow = 2, widths = c(0.6, 0.4), labels = LETTERS[1:4], font.label = list_fontpars,
plot_sgRNAs_per_gene + theme(plot.margin = unit(c(12,12,12,12), "points")),
plot_sgRNA_efficiency + theme(plot.margin = unit(c(26,12,12,12), "points")),
plot_selected_sgRNAs + theme(plot.margin = unit(c(12,-4,12,14), "points")),
plot_sgRNA_correlation + theme(plot.margin = unit(c(26,12,12,12), "points"))
)
dev.off()
null device
1
Export supplemental figure with all ribosomal genes (rpsNN/rplNN).
plot_sgRNAs_ribosome <- df_main %>%
filter(str_detect(sgRNA_target, "rp[sl][0-9]*$")) %>%
filter(condition == "LC, LL") %>%
ggplot(aes(x = time, y = log2FoldChange, color = factor(sgRNA_index))) +
geom_line(size = 1) + geom_point(size = 2) +
facet_wrap(~ sgRNA_target, ncol = 7) +
custom_theme(legend.position = "top") +
scale_color_manual(values = custom_range(5))
print(plot_sgRNAs_ribosome)

Gene fitness calculation
Summarize sgRNA fitness to gene fitness
With the correlation and the efficiency per sgRNA, we can compute the weighted mean of all sgRNAs. For comparison, we also test simple strategies such as the standard arithmetic mean and a top 1 and top 2 sgRNAs strategy. Metrics are calculated for log2 FC, and fitness.
df_controls <- df_main %>% ungroup %>%
filter(str_detect(sgRNA_target, "ctrl[0-9]+$"))
df_gene <- df_main %>%
# keep all annotation columns
group_by(sgRNA_target, sgRNA_type, locus, gene_name, condition,
carbon, light, treatment, time) %>%
# summarize FC and fitness...
summarize(.groups = "drop",
# log2 FC
mean_log2FoldChange = mean(log2FoldChange),
wmean_log2FoldChange = weighted.mean(log2FoldChange, sgRNA_correlation * sgRNA_efficiency),
top1_log2FoldChange = log2FoldChange[which.max(sgRNA_efficiency)],
top2_log2FoldChange = mean(log2FoldChange[order(sgRNA_efficiency, decreasing = TRUE)[1:2]]),
sd_log2FoldChange = sd(log2FoldChange),
# fitness
mean_fitness = mean(fitness),
wmean_fitness = weighted.mean(fitness, sgRNA_correlation * sgRNA_efficiency),
top1_fitness = fitness[which.max(sgRNA_efficiency)],
top2_fitness = mean(fitness[order(sgRNA_efficiency, decreasing = TRUE)[1:2]]),
sd_fitness = sd(fitness),
# apply significance test, Mann-Whitney U test
p_value = wilcox.test(fitness, filter(df_controls, condition == unique(condition))$fitness)$p.value
)
Since statistical significance is tested for many genes in parallel, the p-value obtained from MWU test should be multiple-hypothesis corrected. For this purpose we use the Benjamini-Hochberg method. We also calculate a score taking both effect size and p-value into account, according to the publication from Wang et al., Nat Comm, 2018. This score is simply the absolute fitness score multiplied by the negative log10 p-value.
df_gene <- df_gene %>%
group_by(condition, time) %>%
mutate(
p_value_adj = p.adjust(p_value, method = "BH"),
score = abs(wmean_fitness)*-log10(p_value_adj)
) %>% ungroup
A comparison of log2 FC aggregated by the different method shows clear differences. For the example gene rps10 the weighted mean and the top method give similar results, representative of the stronger influence from highly depleted sgRNA repression strains. The regular mean is robust, but “shallow”, probably underestimating the real effect n fitness. The top 1 method simply picks the most depleted/enriched sgRNA (over all conditions) as representative.
df_gene %>% filter(sgRNA_target == "rps10") %>%
pivot_longer(cols = matches("[n12]_log2FoldChange"),
names_to = "metric", values_to = "log2FoldChange") %>%
mutate(metric = str_remove(metric, "_log2FoldChange")) %>%
ggplot(aes(x = time, y = log2FoldChange,
ymin = log2FoldChange-sd_log2FoldChange,
ymax = log2FoldChange+sd_log2FoldChange, color = fct_inorder(metric))) +
geom_line(size = 1) + geom_point(size = 2) + geom_linerange(size = 1) +
facet_wrap(~ condition, ncol = 4) +
custom_theme(legend.position = "right") +
coord_cartesian(ylim = c(-3.75, 0.75)) +
scale_color_manual(values = custom_range(4))

This plot shows a comparison of the 4 methods for the first 36 genes by alphabetical order, for one selected condition only (1% CO2, BG11, 1,000 µmol photons m-1 s-1). Here we can see that the top1 method is often but not always representative for the gene: For apcD or apcF, it does not seem representative compared to the mean, weighted mean, and top2 methods.
df_gene %>% filter(
gene_name %in% unique(.data[["gene_name"]])[1:36],
condition == "HC, HL"
) %>%
pivot_longer(cols = matches("[n12]_log2FoldChange"), names_to = "metric", values_to = "log2FoldChange") %>%
mutate(metric = str_remove(metric, "_log2FoldChange")) %>%
ggplot(aes(x = time, y = log2FoldChange,
ymin = log2FoldChange-sd_log2FoldChange,
ymax = log2FoldChange+sd_log2FoldChange, color = fct_inorder(metric))) +
geom_line(size = 1) + geom_point(size = 2) + geom_linerange(size = 1) +
facet_wrap(~ sgRNA_target, ncol = 7) +
custom_theme(legend.position = "top") +
coord_cartesian(ylim = c(-5, 5)) +
scale_color_manual(values = custom_range(4))

Global distribution of gene fitness
Global distribution of weighted mean fitness for all genes. Effect of ncRNA repression seems to be much lower than effect of gene repression.
plot_all_fitness_hist <- df_gene %>% filter(time == 0) %>%
ggplot(aes(x = wmean_fitness, fill = sgRNA_type)) +
geom_histogram(bins = 100) +
coord_cartesian(xlim = c(-4, 4), ylim = c(0, 1000)) +
facet_wrap( ~ condition, ncol = 4) +
custom_theme() +
scale_fill_manual(values = custom_colors[c(3:4)])
print(plot_all_fitness_hist)

save_plot(plot_all_fitness_hist, width = 7, height = 5)
Gene fitness vs significance
plot_all_fitness_volc <- df_gene %>% filter(time == 0,
condition %in% c("HC, HL", "LC, LL")) %>%
arrange(sgRNA_type) %>%
ggplot(aes(x = wmean_fitness, y = -log10(p_value_adj), col = sgRNA_type)) +
geom_point(alpha = 0.5, size = 0.5) +
geom_line(data = data.frame(x = c(seq(-8, -0.5, 0.1), seq(0.5, 8, 0.1)),
y = 4/c(seq(8, 0.5, -0.1), seq(0.5, 8, 0.1))),
aes(x = x, y = y, shape = NULL, col = NULL), lty = 2) +
coord_cartesian(xlim = c(-7, 7), ylim = c(0, 4)) +
custom_theme(aspect = 1, legend.position = "left", legend.key.size = unit(0.4, "cm")) +
facet_wrap(~ condition) +
labs(x = "fitness", y = expression("-log"[10]*" p-value")) +
scale_color_manual(values = custom_colors[3:4]) +
scale_shape_manual(values=c(1, 19))
print(plot_all_fitness_volc)

save_plot(plot_all_fitness_volc, width = 6, height = 3)
Behavior of control sgRNAs
Ten sgRNAs were included in the library that have no gene-specific targets. The following plot shows that these negative controls do not have an effect on strain fitness, except probably 2 sgRNAs in one specific condition.
plot_controls_sgRNAs <- df_main %>% filter(grepl("ctrl", sgRNA_target)) %>%
ggplot(aes(x = time, y = log2FoldChange, color = sgRNA_target)) +
geom_line(size = 1) + geom_point(size = 2) + ylim(-5, 5) +
facet_wrap(~ condition, ncol = 4) +
custom_theme() +
scale_color_manual(values = custom_range(10))
print(plot_controls_sgRNAs)

save_plot(plot_controls_sgRNAs, width = 7, height = 5.5)
Gene enrichment
To plot gene fitness for the enzymes of central carbon metabolism, we need a complete list of enzymes and the genes that they are mapped to. To list the different KEGG databases that can be queried, use listDatabases(). Gene-pathway mappings are obtained and merged with pathway names and gene/enzyme names.
# get mapping of pathways for each gene
df_kegg <- keggLink("pathway", "syn") %>%
enframe(name = "locus", value = "kegg_pathway_id") %>%
# get list of pathways with name/ID pairs
left_join(by = "kegg_pathway_id",
keggList("pathway", "syn") %>%
enframe(name = "kegg_pathway_id", value = "kegg_pathway")
) %>%
# get list of gene/enzyme names
left_join(by = "locus",
keggList("syn") %>%
enframe(name = "locus", value = "kegg_gene") %>%
mutate(kegg_gene_short = str_extract(kegg_gene, "^[a-zA-Z0-9]*;") %>%
str_remove(";"))
) %>%
# trim useless prefixes
mutate(
locus = str_remove(locus, "syn:"),
kegg_pathway_id = str_remove(kegg_pathway_id, "path:"),
kegg_pathway = str_remove(kegg_pathway, " - Synechocystis sp. PCC 6803")
)
head(df_kegg)
Fitness per pathway
Sometimes even small effects in fitness can be relevant if several genes of the same pathway (or iso-enzymes) are affected. A simple fitness threshold will not reveal those changes. In such cases a more nuanced approach can be taken, a gene set enrichment analysis (GSEA). Several packages exist to test if functionally related genes are enriched, depleted, or both at the same time / the same conditions.
Before we test for enrichment of associated pathways/GO terms, we can have a look at the general depletion/enrichment per KEGG pathway. The fitness distribution per pathway can be visualized using a violin- or scatter plot.
plot_median_fitness_kegg <- df_gene %>% filter(time == 0) %>%
inner_join(df_kegg, by = "locus") %>%
group_by(kegg_pathway, condition) %>%
summarize(.groups = "drop",
fitness = median(wmean_fitness),
n_genes = n()
) %>% filter(n_genes >= 20) %>%
mutate(kegg_pathway = paste0(str_sub(kegg_pathway, 1, 25), "..")) %>%
mutate(kegg_pathway = fct_reorder(kegg_pathway, fitness, .desc = TRUE)) %>%
ggplot(aes(x = fitness, y = kegg_pathway)) +
geom_boxplot(outlier.shape = NULL, color = grey(0.5), fill = grey(0.9)) +
geom_point(aes(color = condition)) +
geom_vline(xintercept = 0, lty = 2, color = grey(0.5)) +
labs(x = "median fitness", y = "") +
custom_theme(legend.position = c(0.25, 0.25), legend.key.size = unit(0.4, "cm")) +
scale_fill_manual(values = custom_range(11)) +
scale_color_manual(values = custom_range(11))
print(plot_median_fitness_kegg)

Export draft Figure 2 for manuscript. We add photosystem I and II genes as examples for differential depletion. A heatmap.
plot_sgRNAs_ps1 <- df_gene %>%
filter(str_detect(sgRNA_target, "psa[A-Z]*"), time == 0) %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
ggplot(aes(x = condition, y = fct_rev(sgRNA_target), fill = wmean_fitness)) +
geom_tile() + custom_theme() +
labs(title = "Photosystem I", x = "", y = "") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
plot_sgRNAs_ps2 <- df_gene %>%
filter(str_detect(sgRNA_target, "psb[A-Z]*"), time == 0) %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
mutate(sgRNA_target = str_replace(sgRNA_target, "psb13", "psbW")) %>%
ggplot(aes(x = condition, y = fct_rev(sgRNA_target), fill = wmean_fitness)) +
geom_tile() + custom_theme() +
labs(title = "Photosystem II", x = "", y = "") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
ggarrange(ncol = 2, plot_sgRNAs_ps1, plot_sgRNAs_ps2)

svg(filename = "../figures/figure2.svg", width = 8, height = 7)
ggarrange(ncol = 2, widths = c(0.65, 0.35),
ggarrange(nrow = 2, heights = c(0.34, 0.66), labels = LETTERS[1:2], font.label = list_fontpars,
plot_all_fitness_volc + theme(plot.margin = unit(c(14,-8,14,40), "points")),
plot_median_fitness_kegg + theme(plot.margin = unit(c(6,12,12,12), "points"))),
ggarrange(nrow = 2, heights = c(0.4, 0.6), labels = LETTERS[3:4], font.label = list_fontpars,
plot_sgRNAs_ps1 + theme(plot.margin = unit(c(12,0,-14,0), "points")),
plot_sgRNAs_ps2 + theme(plot.margin = unit(c(12,0,0,0), "points"))
)
)
dev.off()
null device
1
Gene enrichment analysis (KEGG)
We use the functions kegga for KEGG enrichment analysis and goana for GO term enrichment from the limma package. Both functions test for over or under-representation of genes associated with certain pathways or GO terms. The functions don’t take the strength of differential fitness into account (DF; the depletion/enrichment over time).
df_kegg_enrichment <- lapply(unique(df_gene$condition), function(cond) {
df_gene %>% filter(
sgRNA_type == "gene", time == 0,
condition == cond) %>%
# filter for differential fitness (DF) genes
filter(!between(wmean_fitness, -2.0, 2.0), !is.na(locus)) %>%
# perform KEGG enrichment
pull(locus) %>% kegga(species.KEGG = "syn") %>%
mutate(condition = cond)
}) %>% bind_rows
head(df_kegg_enrichment)
Now we visualize the pathways that are most enriched for DF genes. It turns out that ribosomal proteins are extremely depleted and therefore score high on the negative log10 p-value for pathway enrichment.
df_kegg_enrichment %>%
rename(kegg_pathway = Pathway) %>%
group_by(kegg_pathway) %>% filter(N >= 20) %>%
select(kegg_pathway, condition, P.DE) %>%
mutate(log10_p_value = -log10(P.DE), .keep = "unused") %>%
mutate(kegg_pathway = paste0(str_sub(kegg_pathway, 1, 25), "..")) %>%
# make correlation plot
pivot_wider(names_from = condition, values_from = log10_p_value) %>%
column_to_rownames(var = "kegg_pathway") %>% as.matrix %>%
corrplot(is.corr = FALSE, tl.col = grey(0.5), tl.cex = 0.8,
col = colorRampPalette(custom_colors[c(1,5,2)])(10), col.lim = c(0, 20))

Unsupervised clustering of genes
Cluster genes by similarity
We can devise a generalized tidyverse friendly function to cluster a name variable by a value, grouped by one or more grouping variables. For example, cluster genes (name) by fitness (value) over several conditions (groups). The output is a factor with re-ordered levels.
fct_cluster <- function(variable, group, value, method = "ward.D2") {
df <- tibble(variable = variable, group = group, value = value)
df <- pivot_wider(df, names_from = group, values_from = value)
mat <- as.matrix(column_to_rownames(df, var = "variable"))
cl <- hclust(dist(mat), method = method)
ord <- order.dendrogram(as.dendrogram(cl))
factor(variable, unique(variable)[ord])
}
Heat map of fitness for all genes and all conditions.
plot_heatmap_all <- df_gene %>% filter(time == 0, !is.na(locus)) %>%
mutate(locus = fct_cluster(locus, condition, wmean_fitness)) %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
ggplot(aes(x = locus, y = condition, fill = wmean_fitness)) +
geom_tile() + custom_theme(legend.pos = "right") +
labs(x = paste0("genes (", length(unique(df_gene$locus)),")"), y = "") +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
print(plot_heatmap_all)

save_plot(plot_heatmap_all, width = 8, height = 2.2)
Now we can plot all genes, a subset with only significant genes, and a dendrogram for clustering. The result is hard to interpret. With some exceptions, most genes are grouped in broad unspecific clusters that do not reveal clear relationships between treatment variables and fitness outcome.
# prepare new df and plot heatmap
df_heatmap <- df_gene %>% filter(time == 0, !is.na(locus)) %>%
group_by(locus) %>% filter(any(!between(wmean_fitness, -4, 4))) %>% ungroup %>%
mutate(locus = fct_cluster(locus, condition, wmean_fitness)) %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 8, 8) %>% replace(., . < -8, -8))
plot_heatmap_sig <- df_heatmap %>%
ggplot(aes(x = locus, y = condition, fill = wmean_fitness)) +
geom_tile() + custom_theme(legend.pos = "right") +
labs(x = paste0("genes (", length(unique(df_gene$locus)),")"), y = "") +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-8, 8))
# prepare dist object for clustering and plot dend
dist_heatmap <- df_heatmap %>% select(locus, condition, wmean_fitness) %>%
pivot_wider(names_from = condition, values_from = wmean_fitness) %>%
column_to_rownames(var = "locus") %>% as.matrix %>%
dist
plot_cluster_dend <- dist_heatmap %>%
hclust(method = "ward.D2") %>% as.dendrogram %>%
set("branches_k_col", custom_colors[1:5], k = 5) %>%
set("branches_lwd", 0.5) %>%
as.ggdend %>%
ggplot(labels = FALSE)
# arrange both on same plot
ggarrange(nrow = 2, heights = c(0.5, 0.5),
plot_cluster_dend + theme(plot.margin = unit(c(0.1, 0.09, -0.15, 0.136),"npc")),
plot_heatmap_sig
)

Gene similarity by dimensionality reduction methods
We use two different dimensionality reduction methods, nMDS and t-SNE. We can check if these methods reproduce the clustering for the significantly regulated genes produced with hclust. Analysis shows that the small clusters are more strongly separated from the rest.
# set a seed to obtain same pattern for stochastic methods
set.seed(123)
# run nMDS analysis
NMDS <- dist_heatmap %>% metaMDS
Run 0 stress 0.08477877
Run 1 stress 0.08485663
... Procrustes: rmse 0.002551692 max resid 0.04275329
Run 2 stress 0.08484567
... Procrustes: rmse 0.001445249 max resid 0.01754732
Run 3 stress 0.09041578
Run 4 stress 0.09773472
Run 5 stress 0.09766743
Run 6 stress 0.09766712
Run 7 stress 0.09766695
Run 8 stress 0.097774
Run 9 stress 0.08581093
Run 10 stress 0.08477915
... Procrustes: rmse 0.0001249276 max resid 0.00204171
... Similar to previous best
Run 11 stress 0.09034496
Run 12 stress 0.08477945
... Procrustes: rmse 0.0001738428 max resid 0.002843804
... Similar to previous best
Run 13 stress 0.08477977
... Procrustes: rmse 0.0002175779 max resid 0.003564318
... Similar to previous best
Run 14 stress 0.08477903
... Procrustes: rmse 0.0001075038 max resid 0.001756538
... Similar to previous best
Run 15 stress 0.08484495
... Procrustes: rmse 0.001423391 max resid 0.01739739
Run 16 stress 0.08485667
... Procrustes: rmse 0.002551594 max resid 0.0427536
Run 17 stress 0.08485605
... Procrustes: rmse 0.002540429 max resid 0.04264689
Run 18 stress 0.08477984
... Procrustes: rmse 0.0002060804 max resid 0.003370265
... Similar to previous best
Run 19 stress 0.09041548
Run 20 stress 0.09286828
*** Solution reached
df_nmds <- NMDS$points %>% as_tibble(rownames = "locus") %>%
left_join(enframe(name = "locus", value = "cluster",
cutreeord(hclust(dist_heatmap, method = "ward.D2"), k = 5)))
Joining, by = "locus"
# run t-SNE analysis
SNE <- dist_heatmap %>% tsne(max_iter = 500, perplexity = 8)
sigma summary: Min. : 0.317869467815397 |1st Qu. : 0.457314965951253 |Median : 0.518197389451857 |Mean : 0.550439569803973 |3rd Qu. : 0.594385229072092 |Max. : 1.44274730511067 |
Epoch: Iteration #100 error is: 15.9553533361405
Epoch: Iteration #200 error is: 0.678497037505085
Epoch: Iteration #300 error is: 0.620797600171921
Epoch: Iteration #400 error is: 0.601017031027447
Epoch: Iteration #500 error is: 0.595616760156116
df_tsne <- SNE %>% setNames(c("x", "y")) %>% as_tibble %>%
mutate(locus = unique(df_heatmap$locus)) %>%
left_join(enframe(name = "locus", value = "cluster",
cutreeord(hclust(dist_heatmap, method = "ward.D2"), k = 5)))
Joining, by = "locus"
plot_nmds <- df_nmds %>%
ggplot(aes(x = MDS1, y = MDS2, color = factor(cluster))) +
geom_point(size = 2) + labs(title = "nMDS") +
custom_theme(legend.position = c(0.85, 0.78)) +
scale_color_manual(values = custom_colors)
plot_tsne <- df_tsne %>%
ggplot(aes(x = V1, y = V2, color = factor(cluster))) +
geom_point(size = 2) + labs(title = "t-SNE") +
custom_theme(legend.position = c(0.85, 0.78)) +
scale_color_manual(values = custom_colors)
ggarrange(ncol = 2, plot_nmds, plot_tsne)

ggsave("../figures/plot_nmds_tsne.svg",
plot = ggarrange(ncol = 2, plot_nmds, plot_tsne),
device = "svg", width = 8, height = 4)
Fit multiple linear regression models
We can find clusters of genes with similar fitness, but it is also important to identify why they cluster together. In order to find out which variables determine the fitness outcome of a gene, we can perform multiple linear regression. Each gene needs to have fitness outcomes annotated with the different (mixed) variables carbon, light, treatment. The latter can be subdivided in individual treatment columns glucose, DCMU, fluctuating light, and so on. Multiple linear regression fits a linear model of the following form to the data:
response ~ intercept + predictor A x slope A + predictor B x slope B x ...
Here, fitness is the response variable, the different conditions are the predictors. It is important to convert the categorical predictors into (numerical) dummy variables. Then for each individual gene, multiple linear models are fitted and the power of each predictor variable to predict the response is extracted.
# fixed model with 6 predictor variables -- dynamic layout would
# be better in future
fit_linreg <- function(y, x1, x2, x3, x4, x5, x6){
fit <- lm(y ~ x1 + x2 + x3 + x4 + x5 + x6)
c(coefficients(fit), summary(fit)$coefficients[, 4],
summary(fit)$r.squared)
}
# recode categorical to numerical (dummy) variables
df_linreg <- df_gene %>%
filter(!is.na(locus)) %>%
select(locus, carbon, light, treatment, wmean_fitness) %>% distinct %>%
mutate(
carbon = recode(carbon, `HC` = 1, `LC` = 0),
light = recode(light, `LL` = 0, `IL` = 0.5, `HL` = 1)) %>%
mutate(dummy = 1, treatment = replace(treatment, treatment == "", "-")) %>%
pivot_wider(names_from = treatment, values_from = dummy, values_fill = 0) %>%
mutate(`+G` = `+G` + `+D, +G`) %>% rename(`+D` = `+D, +G`) %>% select(-`-`) %>%
# fit model
group_by(locus) %>%
summarize(coefficient = fit_linreg(wmean_fitness, carbon, light, `-N`, `+FL`, `+G`, `+D`),
.groups = "keep") %>% #unnest(coefficient) %>%
mutate(treatment = c(rep(c("intercept", "carbon", "light", "-N", "+FL", "+G", "+D"), 2) %>%
paste0(rep(c("", "pval_"), each = 7), .), "r_squared"))
Now we can overlay the information of the best predictor variable on the cluster map produced by tSNE, for example, and this way identify groups of genes regulated in a similar degree, by similar variables.
plot_tsne_linreg <- df_tsne %>%
inner_join(df_linreg, by = "locus") %>%
left_join(select(df_gene, locus, sgRNA_target) %>% distinct, by = "locus") %>%
filter(!str_detect(treatment, "intercept|pval|r_squared")) %>%
mutate(sgRNA_target = if_else(abs(coefficient) > 2, sgRNA_target, "")) %>%
mutate(point_size = abs(coefficient),
coefficient = coefficient %>% replace(., . > 5, 5) %>% replace(., . < -5, -5)) %>%
ggplot(aes(x = V1, y = V2, size = point_size,
color = coefficient, label = sgRNA_target)) +
geom_point() +
labs(title = "t-SNE clustering of DF genes",
subtitle = paste0("dot color/size encodes effect of variable, n = ", nrow(df_tsne))) +
custom_theme(aspect = 1) +
scale_color_gradientn(limits = c(-5, 5),
colours = c(custom_colors[1], grey(0.6, 0.8), custom_colors[2])) +
scale_size_continuous(range = c(1, 6)) +
geom_text_repel(size = 3, max.overlaps = 50) +
facet_wrap( ~ treatment, ncol = 2)
print(plot_tsne_linreg)

This strategy reveals a list of interesting condition-specific genes:
- Nitrogen limitation:
ssr3532 - unknown short protein, strongest known interaction in STRING with GlsA glutaminase
- Fluctuating light:
sll1521 - Putative diflavin flavoprotein A3 (dfa3), negatively corr. with fitness
sll0217 Putative diflavin flavoprotein A2 (dfa2), positively corr. with fitness
- Mixotrophy:
sll0593 - glk, glucokinase, catalyzes P-ylation of Glc to G6P
sll1533 - pilT, fimbria assembly, mobility, Glc transport or sensing?
ssl3364 - unknown short protein, strongly interacts with RbcX, RbcR, Prk. Important for C-metabolism adapation?
- Light:
ssr2142 ycf19, short unknown protein, interacts with psbO and Tat membrane protein insertion system,
slr0963 sir, sulfite reductase, ferredoxin H2O + HS + ferredoxin <-> H+ + reduced ferredoxin + sulfite, strongly interacts with other proteins in sulfur metabolism, specifically related to cofactor biosynthesis, cobalamin (vitamin B12) and siroheme
- Light, mixotrophy, heterotrophy: cluster of photosynthesis related genes increase fitness when KOed: apcA,D,E, psbB,C,D
- Carbon:
sll0217 Putative diflavin flavoprotein A2 (dfa2), KO negatively correlated with fitness with C, positive with +FL
sll0218 same behavior as dfa2, interacts with dfa2,4, contributes to PSII stabilization, Bersanini et al., 2017
List of genes with strong fitness correlation
The table with linear regression coefficients and p-values is reshaped to long format for better readability. The kableExtra package is used to color cells for easier recognition. Then we subset the table for each treatment in order to spot the most interesting genes.
df_linreg_wide <- df_linreg %>%
pivot_wider(names_from = treatment, values_from = coefficient) %>%
left_join(select(df_gene, locus, sgRNA_target) %>% distinct, by = "locus") %>%
select(-matches("intercept")) %>%
filter(if_any(matches("^(carb|light|\\-|\\+)"), ~ abs(.) > 2)) %>%
mutate(across(matches("carb|light|\\-|\\+"), ~ round(., 3))) %>%
ungroup %>% select(sgRNA_target, locus, matches("."))
color_table <- function(df, variable) {
filter(df, abs(.data[[variable]]) > 2) %>%
select(matches("^(sg|loc|r_s|carb|light|\\-|\\+)") | all_of(paste0("pval_", variable))) %>%
arrange(desc(.data[[variable]])) %>%
mutate(across(3:8, ~ cell_spec(., "html", color = "white",
background = spec_color(., option = "E", scale = c(-5.5, 5.5)),
bold = TRUE))) %>%
kbl(format = "html", escape = F) %>%
kable_paper("striped", full_width = F)
}
df_linreg_wide %>% color_table("carbon")
| sgRNA_target |
locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
pval_carbon |
| sll0364 |
sll0364 |
2.834 |
-2.812 |
-0.108 |
1.758 |
0.601 |
3.578 |
0.8717770 |
0.024 |
| slr1095 |
slr1095 |
2.429 |
-2.1 |
-1.284 |
-1.592 |
-0.274 |
1.777 |
0.6370579 |
0.083 |
| sll1734 |
sll1734 |
2.426 |
-1.267 |
-0.478 |
-1.625 |
0.837 |
1.183 |
0.6560893 |
0.099 |
| slr0211 |
slr0211 |
2.354 |
0.469 |
-0.044 |
-0.362 |
0.786 |
1.884 |
0.8217569 |
0.022 |
| ftsZ |
sll1633 |
2.34 |
-2.314 |
-1.677 |
-1.439 |
0.318 |
2.136 |
0.9249829 |
0.006 |
| ndhD3 |
sll1733 |
2.326 |
-1.213 |
-0.489 |
-1.392 |
0.67 |
1.172 |
0.6640536 |
0.085 |
| ssr3532 |
ssr3532 |
2.303 |
-1.661 |
-3.733 |
-1.464 |
0.702 |
1.64 |
0.6071408 |
0.163 |
| ndhF2 |
sll1732 |
2.163 |
-0.874 |
-0.602 |
-1.431 |
1.022 |
1.124 |
0.6649520 |
0.103 |
| slr1818 |
slr1818 |
2.159 |
-1.144 |
-1.369 |
-1.55 |
-0.087 |
1.675 |
0.5831706 |
0.116 |
| sll0488 |
sll0488 |
2.151 |
-1.322 |
-1.418 |
-1.434 |
0.047 |
1.624 |
0.6312515 |
0.090 |
| sll0481 |
sll0481 |
2.141 |
-3.371 |
-1.601 |
-1.335 |
0.485 |
1.056 |
0.9686290 |
0.002 |
| sll0995 |
sll0995 |
2.101 |
-0.278 |
-1.933 |
-1.322 |
0.722 |
1.384 |
0.6077491 |
0.132 |
| cmpB |
slr0041 |
2.041 |
-1.904 |
-0.766 |
-1.032 |
0.133 |
1.15 |
0.5577051 |
0.126 |
| sir |
slr0963 |
-2.034 |
4.169 |
1.41 |
1.489 |
-0.345 |
-0.303 |
0.8364473 |
0.048 |
| rpl24 |
sll1807 |
-2.081 |
0.994 |
0.573 |
1.814 |
0.023 |
-1.225 |
0.4871941 |
0.200 |
| rps9 |
sll1822 |
-2.087 |
0.874 |
0.64 |
1.319 |
-0.719 |
-0.988 |
0.5755515 |
0.137 |
| slr6107 |
slr6107 |
-2.108 |
1.215 |
1.13 |
1.151 |
-0.237 |
-1.246 |
0.5493324 |
0.124 |
| slr0272 |
slr0272 |
-2.122 |
1.596 |
1.27 |
1.27 |
-1.038 |
-0.791 |
0.6159525 |
0.127 |
| leuD |
sll1444 |
-2.209 |
2.505 |
-0.124 |
1.64 |
-1.451 |
-0.038 |
0.6714334 |
0.146 |
| rps17 |
ssl3437 |
-2.241 |
0.79 |
1.027 |
1.922 |
-0.148 |
-1.382 |
0.5117852 |
0.184 |
| slr0007 |
slr0007 |
-2.308 |
1.932 |
0.695 |
2.012 |
-0.851 |
-1.068 |
0.6792795 |
0.109 |
| slr1938 |
slr1938 |
-2.311 |
0.268 |
0.592 |
1.075 |
-0.772 |
-1.967 |
0.7489109 |
0.051 |
| rpl35 |
ssl1426 |
-2.361 |
1.851 |
1.677 |
1.293 |
-0.482 |
-1.844 |
0.5899106 |
0.124 |
| sll0218 |
sll0218 |
-2.376 |
1.766 |
0.727 |
1.766 |
-0.193 |
-1.1 |
0.6105884 |
0.110 |
| sll0217 |
sll0217 |
-2.418 |
1.723 |
0.954 |
2.228 |
-0.286 |
-1.152 |
0.6281035 |
0.118 |
| sll0933 |
sll0933 |
-2.634 |
1.271 |
0.081 |
2.115 |
-0.633 |
-0.702 |
0.6558952 |
0.105 |
| slr1245 |
slr1245 |
-2.694 |
1.826 |
1.245 |
-0.805 |
1.092 |
-3.224 |
0.8325233 |
0.019 |
| rps15 |
ssl1784 |
-2.769 |
1.647 |
1.312 |
2.437 |
-0.758 |
-1.499 |
0.7223751 |
0.075 |
df_linreg_wide %>% color_table("light")
| sgRNA_target |
locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
pval_light |
| apcE |
slr0335 |
-0.681 |
5.071 |
0.314 |
1.093 |
3.962 |
1.65 |
0.8255851 |
0.055 |
| apcA |
slr2067 |
0.027 |
4.441 |
0.576 |
0.809 |
3.301 |
2.922 |
0.7380667 |
0.127 |
| sll1878 |
sll1878 |
-1.585 |
4.291 |
2.016 |
2.165 |
1.301 |
-0.213 |
0.7988744 |
0.024 |
| sir |
slr0963 |
-2.034 |
4.169 |
1.41 |
1.489 |
-0.345 |
-0.303 |
0.8364473 |
0.031 |
| cpcB |
sll1577 |
0.12 |
4.032 |
0.141 |
0.74 |
2.544 |
2.476 |
0.8276584 |
0.053 |
| murC |
slr1423 |
0.232 |
3.875 |
0.345 |
0.027 |
1.164 |
-0.248 |
0.6419118 |
0.096 |
| sll1378 |
sll1378 |
-0.955 |
3.701 |
-0.4 |
1.2 |
2.782 |
0.937 |
0.9545533 |
0.006 |
| sll1879 |
sll1879 |
-0.306 |
3.662 |
-0.894 |
-0.217 |
0.681 |
-0.74 |
0.7312512 |
0.074 |
| hitB |
slr0327 |
-1.726 |
3.606 |
1.56 |
1.726 |
0.576 |
-0.39 |
0.6687398 |
0.087 |
| cpcA |
sll1578 |
0.11 |
3.57 |
0.261 |
0.609 |
2.255 |
2.162 |
0.8434275 |
0.044 |
| slr0947 |
slr0947 |
0.18 |
3.422 |
0.454 |
0.742 |
1.52 |
0.613 |
0.7928550 |
0.028 |
| slr1990 |
slr1990 |
-0.768 |
3.219 |
0.197 |
0.994 |
2.472 |
2.429 |
0.8920252 |
0.044 |
| slr1102 |
slr1102 |
0.056 |
3.162 |
-0.118 |
0.511 |
2.737 |
0.819 |
0.8207709 |
0.065 |
| amiC |
slr0447 |
-1.235 |
3.159 |
0.549 |
-0.175 |
1.309 |
0.402 |
0.9300166 |
0.007 |
| sll6055 |
sll6055 |
-1.122 |
3.123 |
0.558 |
0.514 |
2.406 |
2.128 |
0.8376624 |
0.091 |
| cyp2 |
slr0574 |
-0.6 |
3.073 |
0.867 |
1.027 |
2.347 |
-0.438 |
0.9444943 |
0.003 |
| sll1945 |
sll1945 |
-0.194 |
3.072 |
0.642 |
0.625 |
0.063 |
1.299 |
0.7068087 |
0.059 |
| sll0689 |
sll0689 |
-0.125 |
3.062 |
0.19 |
0.41 |
0.027 |
0.162 |
0.5002592 |
0.178 |
| narB |
sll1454 |
-0.223 |
3.011 |
0.044 |
0.014 |
0.034 |
0.634 |
0.8309629 |
0.025 |
| def |
slr1549 |
0.929 |
2.973 |
1.091 |
-0.039 |
1.821 |
0.97 |
0.7394573 |
0.103 |
| slr7096 |
slr7096 |
-0.452 |
2.972 |
-0.83 |
0.696 |
-0.16 |
0.433 |
0.9130593 |
0.010 |
| psbJ |
smr0008 |
0.192 |
2.968 |
0.293 |
0.419 |
3.216 |
3.222 |
0.8923081 |
0.090 |
| slr1505 |
slr1505 |
-0.682 |
2.926 |
0.456 |
0.864 |
2.222 |
3.068 |
0.9263147 |
0.032 |
| slr0483 |
slr0483 |
0.315 |
2.922 |
0.533 |
0.633 |
1.047 |
0.53 |
0.9363725 |
0.003 |
| nirA |
slr0898 |
-1.083 |
2.919 |
-0.106 |
0.515 |
-0.343 |
-0.004 |
0.7628800 |
0.063 |
| apcB |
slr1986 |
0.627 |
2.876 |
0.319 |
0.432 |
2.241 |
2.188 |
0.7619664 |
0.129 |
| slr0734 |
slr0734 |
0.52 |
2.819 |
0.607 |
0.753 |
1.99 |
2.183 |
0.8211958 |
0.067 |
| slr1170 |
slr1170 |
-0.807 |
2.766 |
-0.676 |
0.308 |
0.668 |
-0.598 |
0.7363329 |
0.070 |
| ycf38 |
sll0760 |
-0.445 |
2.748 |
-0.294 |
0.164 |
0.823 |
1.469 |
0.6739376 |
0.122 |
| ssl0331 |
ssl0331 |
-1.177 |
2.738 |
1.195 |
1.389 |
0.463 |
-1.187 |
0.8598838 |
0.019 |
| slr1693 |
slr1693 |
-1.723 |
2.719 |
0.576 |
0.898 |
-0.397 |
-1.131 |
0.8525814 |
0.045 |
| psbO |
sll0427 |
0.64 |
2.707 |
0.222 |
0.633 |
3.339 |
1.65 |
0.8838887 |
0.078 |
| psbD |
sll0849 |
-0.861 |
2.664 |
1.002 |
-0.005 |
3.231 |
3.101 |
0.8557861 |
0.192 |
| slr1692 |
slr1692 |
0.437 |
2.655 |
0.33 |
0.811 |
1.705 |
1.623 |
0.8429081 |
0.039 |
| hemB |
sll1994 |
-0.063 |
2.652 |
-0.914 |
0.697 |
0.646 |
0.836 |
0.6646820 |
0.110 |
| slr2042 |
slr2042 |
-0.364 |
2.633 |
0.809 |
1.148 |
1.345 |
-1.302 |
0.8793763 |
0.008 |
| cpcG |
slr2051 |
-0.205 |
2.621 |
0.183 |
0.472 |
2.294 |
1.485 |
0.9132484 |
0.027 |
| slr1302 |
slr1302 |
-0.999 |
2.591 |
0.061 |
0.454 |
1.365 |
-0.191 |
0.8534901 |
0.026 |
| moeB |
sll1536 |
-1.04 |
2.579 |
1.069 |
0.234 |
-0.366 |
-0.368 |
0.7776446 |
0.054 |
| sll0148 |
sll0148 |
0.357 |
2.556 |
-0.37 |
-0.143 |
1.982 |
0.077 |
0.7719391 |
0.094 |
| plsX |
slr1510 |
0.976 |
2.549 |
0.169 |
-0.006 |
0.186 |
1.868 |
0.6005493 |
0.205 |
| sll6109 |
sll6109 |
-0.938 |
2.516 |
0.586 |
0.717 |
0.079 |
0.159 |
0.8704727 |
0.013 |
| cysH |
slr1791 |
-1.968 |
2.512 |
0.674 |
1.242 |
0.068 |
-0.998 |
0.8428947 |
0.057 |
| leuD |
sll1444 |
-2.209 |
2.505 |
-0.124 |
1.64 |
-1.451 |
-0.038 |
0.6714334 |
0.313 |
| trxA3 |
slr0623 |
-1.336 |
2.493 |
1.078 |
1.881 |
0.16 |
-0.637 |
0.8097690 |
0.057 |
| nrtB |
sll1451 |
-0.107 |
2.485 |
0.272 |
0.388 |
-0.244 |
1.229 |
0.8884861 |
0.012 |
| slr1841 |
slr1841 |
-0.166 |
2.451 |
1.171 |
0.655 |
0.131 |
0.426 |
0.7466448 |
0.040 |
| sll2003 |
sll2003 |
-0.423 |
2.396 |
0.519 |
0.236 |
1.625 |
0.597 |
0.8779608 |
0.022 |
| drgA |
slr1719 |
-1.649 |
2.368 |
0.596 |
1.457 |
-0.317 |
-0.446 |
0.6695048 |
0.187 |
| sll1380 |
sll1380 |
-0.367 |
2.364 |
0.551 |
0.741 |
1.707 |
0.674 |
0.9376518 |
0.006 |
| petH |
slr1643 |
-1.23 |
2.351 |
1.048 |
1.592 |
2.039 |
-2.97 |
0.7531722 |
0.109 |
| sll1500 |
sll1500 |
-1.256 |
2.346 |
-0.004 |
0.248 |
-0.861 |
-0.634 |
0.7141056 |
0.150 |
| sll1304 |
sll1304 |
-0.655 |
2.334 |
0.667 |
0.558 |
0.589 |
0.622 |
0.8743538 |
0.010 |
| sll0301 |
sll0301 |
0.045 |
2.33 |
-0.396 |
-0.094 |
2.231 |
0.605 |
0.9108020 |
0.034 |
| slr0950 |
slr0950 |
-0.06 |
2.276 |
0.509 |
0.314 |
1.55 |
0.089 |
0.8557459 |
0.020 |
| hemC |
slr1887 |
-0.702 |
2.265 |
0.206 |
0.998 |
0.118 |
0.461 |
0.6255683 |
0.111 |
| trpF |
sll0356 |
0.03 |
2.257 |
0.118 |
0.505 |
-0.803 |
1.243 |
0.7586823 |
0.082 |
| ndhF |
slr2009 |
0.221 |
2.237 |
0.905 |
0.613 |
2.258 |
3.08 |
0.9355070 |
0.043 |
| slr1042 |
slr1042 |
0.571 |
2.208 |
0.633 |
0.498 |
0.785 |
0.821 |
0.5506013 |
0.163 |
| proC |
slr0661 |
-0.746 |
2.205 |
0.115 |
-0.077 |
1.399 |
1.48 |
0.8763769 |
0.057 |
| rpiA |
slr0194 |
-0.278 |
2.194 |
1.674 |
1.505 |
2.476 |
-1.452 |
0.9605876 |
0.003 |
| thrA |
sll0455 |
0.548 |
2.169 |
-0.427 |
-0.459 |
0.803 |
0.359 |
0.6473974 |
0.170 |
| glnA |
slr1756 |
-0.699 |
2.114 |
0.035 |
0.284 |
-1.379 |
0.004 |
0.8295902 |
0.089 |
| slr0519 |
slr0519 |
-1.506 |
2.09 |
1.043 |
1.988 |
0.287 |
-1.1 |
0.6946988 |
0.190 |
| ssl1918 |
ssl1918 |
-0.063 |
2.086 |
0.326 |
0.449 |
1.257 |
0.418 |
0.7483282 |
0.054 |
| sll0847 |
sll0847 |
-0.875 |
2.075 |
-0.013 |
0.095 |
0.622 |
0.374 |
0.7363211 |
0.083 |
| ccmK4 |
slr1839 |
-0.233 |
2.073 |
0.584 |
0.413 |
2.248 |
0.326 |
0.7885394 |
0.106 |
| pdhB |
sll1721 |
-0.862 |
2.072 |
1.323 |
1.246 |
0.616 |
-0.273 |
0.7513861 |
0.044 |
| sll1025 |
sll1025 |
0.419 |
2.061 |
0.377 |
0.473 |
1.319 |
0.495 |
0.7599508 |
0.059 |
| prc |
slr1751 |
0.376 |
2.057 |
1.334 |
0.611 |
0.429 |
0.649 |
0.5909157 |
0.136 |
| slr0771 |
slr0771 |
-0.084 |
2.042 |
0.311 |
0.342 |
0.239 |
-0.095 |
0.7574635 |
0.037 |
| tufA |
sll1099 |
-1.461 |
2.041 |
1.399 |
2.218 |
0.036 |
-1.078 |
0.7057584 |
0.217 |
| fabF |
sll1069 |
0.361 |
2.04 |
1.575 |
0.069 |
0.345 |
-0.025 |
0.8170498 |
0.048 |
| aroB |
slr2130 |
-1.206 |
2.02 |
-0.656 |
0.772 |
-0.523 |
-0.045 |
0.8102623 |
0.094 |
| nrtA |
sll1450 |
-0.214 |
2.019 |
0.246 |
0.238 |
-0.409 |
1.571 |
0.8602072 |
0.026 |
| hhoB |
sll1427 |
-0.04 |
2.017 |
-0.198 |
0.237 |
2.207 |
0.158 |
0.7804217 |
0.129 |
| slr0813 |
slr0813 |
1.839 |
-2.009 |
0.304 |
0.354 |
-0.981 |
0.595 |
0.7628332 |
0.187 |
| slr1783 |
slr1783 |
-0.973 |
-2.014 |
-0.805 |
-0.173 |
-0.65 |
0.265 |
0.8726587 |
0.038 |
| sll0176 |
sll0176 |
-0.408 |
-2.085 |
-2.021 |
-1.717 |
-1.526 |
0.952 |
0.6551709 |
0.151 |
| slr1095 |
slr1095 |
2.429 |
-2.1 |
-1.284 |
-1.592 |
-0.274 |
1.777 |
0.6370579 |
0.325 |
| clpP4 |
sll0534 |
-0.193 |
-2.123 |
-1.24 |
-0.652 |
-0.922 |
-0.194 |
0.7367912 |
0.042 |
| sll5063 |
sll5063 |
-1.163 |
-2.128 |
-0.35 |
0.218 |
-0.272 |
0.018 |
0.8440277 |
0.070 |
| mraY |
sll0657 |
1.211 |
-2.188 |
-0.758 |
-0.99 |
-0.843 |
0.787 |
0.8718734 |
0.017 |
| sll0162 |
sll0162 |
0.229 |
-2.203 |
-0.403 |
-0.234 |
-0.405 |
0.062 |
0.6347022 |
0.078 |
| fabF2 |
slr1332 |
0.401 |
-2.251 |
0.251 |
0.187 |
-2.097 |
0.38 |
0.9209766 |
0.021 |
| slr1098 |
slr1098 |
0.903 |
-2.261 |
-0.199 |
-0.434 |
0.479 |
-2.543 |
0.4195152 |
0.403 |
| sll1757 |
sll1757 |
0.783 |
-2.271 |
-0.494 |
-0.752 |
-1.562 |
-0.125 |
0.5383796 |
0.200 |
| ftsZ |
sll1633 |
2.34 |
-2.314 |
-1.677 |
-1.439 |
0.318 |
2.136 |
0.9249829 |
0.039 |
| gcvP |
slr0293 |
1.861 |
-2.335 |
-1.541 |
-0.695 |
-0.558 |
1.696 |
0.9451148 |
0.007 |
| slr0484 |
slr0484 |
0.087 |
-2.342 |
-0.79 |
0.038 |
-0.367 |
-0.061 |
0.7395823 |
0.049 |
| ssr0657 |
ssr0657 |
0.935 |
-2.358 |
-0.659 |
-0.992 |
-2.021 |
1.496 |
0.9170055 |
0.009 |
| sll1915 |
sll1915 |
0.381 |
-2.385 |
-0.147 |
-0.431 |
-1.437 |
-0.118 |
0.6397709 |
0.110 |
| dnaJ4 |
sll0897 |
0.955 |
-2.471 |
-0.087 |
0.165 |
-0.67 |
1.728 |
0.9304110 |
0.007 |
| psaK |
ssr0390 |
1.17 |
-2.479 |
-1.404 |
-0.784 |
-0.729 |
0.717 |
0.6971797 |
0.062 |
| ppiB |
sll0227 |
0.735 |
-2.528 |
-1.035 |
-0.173 |
-0.759 |
0.306 |
0.8520480 |
0.013 |
| slr0643 |
slr0643 |
0.127 |
-2.545 |
-0.449 |
0.098 |
-0.647 |
0.279 |
0.5529642 |
0.140 |
| sll0498 |
sll0498 |
0.407 |
-2.577 |
-1.166 |
-1.108 |
-2.148 |
-1.967 |
0.8482665 |
0.063 |
| mrdB |
slr1267 |
0.179 |
-2.655 |
-0.252 |
-0.24 |
-1.365 |
0.704 |
0.8603778 |
0.014 |
| rpoE |
slr1545 |
0.874 |
-2.759 |
-0.425 |
0.6 |
-1.246 |
1.277 |
0.5615657 |
0.194 |
| sll0364 |
sll0364 |
2.834 |
-2.812 |
-0.108 |
1.758 |
0.601 |
3.578 |
0.8717770 |
0.119 |
| clpX |
sll0535 |
0.736 |
-2.966 |
-1.054 |
-1.121 |
-1.503 |
0.01 |
0.7464408 |
0.035 |
| sll0481 |
sll0481 |
2.141 |
-3.371 |
-1.601 |
-1.335 |
0.485 |
1.056 |
0.9686290 |
0.002 |
| sll0877 |
sll0877 |
1.76 |
-3.694 |
-2 |
-1.39 |
-1.273 |
0.847 |
0.7839636 |
0.032 |
| ycf19 |
ssr2142 |
1.071 |
-3.914 |
-0.278 |
0.284 |
-2.735 |
1.742 |
0.5878787 |
0.184 |
df_linreg_wide %>% color_table("-N")
| sgRNA_target |
locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
pval_-N |
| atpA |
sll1326 |
-0.989 |
-0.069 |
2.047 |
0.064 |
0.272 |
-1.903 |
0.3425321 |
0.396 |
| sll1878 |
sll1878 |
-1.585 |
4.291 |
2.016 |
2.165 |
1.301 |
-0.213 |
0.7988744 |
0.179 |
| sll0176 |
sll0176 |
-0.408 |
-2.085 |
-2.021 |
-1.717 |
-1.526 |
0.952 |
0.6551709 |
0.168 |
| slr1079 |
slr1079 |
1.742 |
-1.553 |
-2.022 |
-1.193 |
-0.394 |
1.843 |
0.5565884 |
0.304 |
| sll5046 |
sll5046 |
1.34 |
-1.273 |
-2.118 |
-0.985 |
-0.329 |
1.365 |
0.6360797 |
0.152 |
| ssr3532 |
ssr3532 |
2.303 |
-1.661 |
-3.733 |
-1.464 |
0.702 |
1.64 |
0.6071408 |
0.201 |
df_linreg_wide %>% color_table("+FL")
| sgRNA_target |
locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
pval_+FL |
| rps15 |
ssl1784 |
-2.769 |
1.647 |
1.312 |
2.437 |
-0.758 |
-1.499 |
0.7223751 |
0.204 |
| pyrG |
sll1443 |
-1.738 |
0.567 |
-0.111 |
2.307 |
-1.107 |
-0.398 |
0.6651024 |
0.212 |
| sll0217 |
sll0217 |
-2.418 |
1.723 |
0.954 |
2.228 |
-0.286 |
-1.152 |
0.6281035 |
0.258 |
| tufA |
sll1099 |
-1.461 |
2.041 |
1.399 |
2.218 |
0.036 |
-1.078 |
0.7057584 |
0.112 |
| sll1878 |
sll1878 |
-1.585 |
4.291 |
2.016 |
2.165 |
1.301 |
-0.213 |
0.7988744 |
0.085 |
| sll0933 |
sll0933 |
-2.634 |
1.271 |
0.081 |
2.115 |
-0.633 |
-0.702 |
0.6558952 |
0.294 |
| fus2 |
sll1098 |
-1.02 |
1.482 |
0.038 |
2.077 |
0.13 |
-0.815 |
0.7239763 |
0.087 |
| slr0007 |
slr0007 |
-2.308 |
1.932 |
0.695 |
2.012 |
-0.851 |
-1.068 |
0.6792795 |
0.267 |
| ribC |
sll0300 |
-1.073 |
-0.365 |
-0.267 |
-2.057 |
-0.721 |
-0.724 |
0.7973653 |
0.037 |
| entC |
slr0817 |
-0.511 |
-0.844 |
-0.456 |
-2.123 |
2.558 |
0.74 |
0.9773035 |
0.009 |
| ribA |
sll1894 |
-0.927 |
-0.39 |
-0.139 |
-2.37 |
-0.725 |
-0.64 |
0.6200967 |
0.101 |
| sll1521 |
sll1521 |
-0.353 |
-0.754 |
-0.52 |
-3.993 |
-0.671 |
-0.342 |
0.9636366 |
0.001 |
df_linreg_wide %>% color_table("+G")
| sgRNA_target |
locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
pval_+G |
| apcE |
slr0335 |
-0.681 |
5.071 |
0.314 |
1.093 |
3.962 |
1.65 |
0.8255851 |
0.056 |
| dgt |
sll0398 |
-0.463 |
0.606 |
0.269 |
1.052 |
3.782 |
-0.261 |
0.9841219 |
0.000 |
| psbO |
sll0427 |
0.64 |
2.707 |
0.222 |
0.633 |
3.339 |
1.65 |
0.8838887 |
0.021 |
| apcA |
slr2067 |
0.027 |
4.441 |
0.576 |
0.809 |
3.301 |
2.922 |
0.7380667 |
0.142 |
| psbC |
sll0851 |
-0.001 |
1.691 |
0.24 |
0.281 |
3.288 |
3.75 |
0.9364716 |
0.019 |
| sll1496 |
sll1496 |
1.499 |
-1.248 |
-0.507 |
-0.657 |
3.237 |
-2.22 |
0.9150619 |
0.013 |
| psbD |
sll0849 |
-0.861 |
2.664 |
1.002 |
-0.005 |
3.231 |
3.101 |
0.8557861 |
0.072 |
| psbJ |
smr0008 |
0.192 |
2.968 |
0.293 |
0.419 |
3.216 |
3.222 |
0.8923081 |
0.037 |
| slr0758 |
slr0758 |
-0.644 |
1.944 |
0.251 |
-0.176 |
2.791 |
-1.499 |
0.9317621 |
0.005 |
| sll1378 |
sll1378 |
-0.955 |
3.701 |
-0.4 |
1.2 |
2.782 |
0.937 |
0.9545533 |
0.006 |
| slr1102 |
slr1102 |
0.056 |
3.162 |
-0.118 |
0.511 |
2.737 |
0.819 |
0.8207709 |
0.050 |
| sll0556 |
sll0556 |
1.268 |
0.982 |
0.25 |
-0.377 |
2.727 |
0.121 |
0.7859377 |
0.060 |
| slr2070 |
slr2070 |
0.846 |
-0.597 |
-0.408 |
-0.608 |
2.711 |
-1.752 |
0.7416662 |
0.075 |
| cbbA |
sll0018 |
-0.644 |
1.164 |
0.415 |
0.545 |
2.607 |
2.054 |
0.9537621 |
0.008 |
| entC |
slr0817 |
-0.511 |
-0.844 |
-0.456 |
-2.123 |
2.558 |
0.74 |
0.9773035 |
0.005 |
| cpcB |
sll1577 |
0.12 |
4.032 |
0.141 |
0.74 |
2.544 |
2.476 |
0.8276584 |
0.093 |
| ssr2062 |
ssr2062 |
0.5 |
0.071 |
0.167 |
-0.086 |
2.528 |
-1.616 |
0.9322018 |
0.004 |
| rpiA |
slr0194 |
-0.278 |
2.194 |
1.674 |
1.505 |
2.476 |
-1.452 |
0.9605876 |
0.001 |
| slr1990 |
slr1990 |
-0.768 |
3.219 |
0.197 |
0.994 |
2.472 |
2.429 |
0.8920252 |
0.047 |
| trx |
sll1057 |
-0.491 |
1.161 |
-0.106 |
0.132 |
2.417 |
0.6 |
0.9611652 |
0.003 |
| sll6055 |
sll6055 |
-1.122 |
3.123 |
0.558 |
0.514 |
2.406 |
2.128 |
0.8376624 |
0.094 |
| psbH |
ssl2598 |
0.265 |
1.696 |
0.215 |
-0.134 |
2.349 |
1.235 |
0.8920776 |
0.024 |
| cyp2 |
slr0574 |
-0.6 |
3.073 |
0.867 |
1.027 |
2.347 |
-0.438 |
0.9444943 |
0.003 |
| ccsA |
sll1513 |
0.287 |
1.628 |
0.096 |
0.428 |
2.321 |
0.814 |
0.9010260 |
0.013 |
| cpcC2 |
sll1579 |
-0.036 |
1.144 |
0.101 |
0.224 |
2.32 |
0.641 |
0.9783295 |
0.001 |
| cpcG |
slr2051 |
-0.205 |
2.621 |
0.183 |
0.472 |
2.294 |
1.485 |
0.9132484 |
0.019 |
| sll0062 |
sll0062 |
0.605 |
1.181 |
0.165 |
0.639 |
2.262 |
1.004 |
0.8522771 |
0.027 |
| ndhF |
slr2009 |
0.221 |
2.237 |
0.905 |
0.613 |
2.258 |
3.08 |
0.9355070 |
0.020 |
| cpcA |
sll1578 |
0.11 |
3.57 |
0.261 |
0.609 |
2.255 |
2.162 |
0.8434275 |
0.079 |
| ccmK4 |
slr1839 |
-0.233 |
2.073 |
0.584 |
0.413 |
2.248 |
0.326 |
0.7885394 |
0.045 |
| apcB |
slr1986 |
0.627 |
2.876 |
0.319 |
0.432 |
2.241 |
2.188 |
0.7619664 |
0.130 |
| sll0301 |
sll0301 |
0.045 |
2.33 |
-0.396 |
-0.094 |
2.231 |
0.605 |
0.9108020 |
0.018 |
| slr1505 |
slr1505 |
-0.682 |
2.926 |
0.456 |
0.864 |
2.222 |
3.068 |
0.9263147 |
0.035 |
| hhoB |
sll1427 |
-0.04 |
2.017 |
-0.198 |
0.237 |
2.207 |
0.158 |
0.7804217 |
0.056 |
| rub |
slr2033 |
0.214 |
1.429 |
0.237 |
-0.416 |
2.109 |
1.991 |
0.9937546 |
0.000 |
| petH |
slr1643 |
-1.23 |
2.351 |
1.048 |
1.592 |
2.039 |
-2.97 |
0.7531722 |
0.085 |
| ssr0657 |
ssr0657 |
0.935 |
-2.358 |
-0.659 |
-0.992 |
-2.021 |
1.496 |
0.9170055 |
0.007 |
| fabF2 |
slr1332 |
0.401 |
-2.251 |
0.251 |
0.187 |
-2.097 |
0.38 |
0.9209766 |
0.012 |
| sll0498 |
sll0498 |
0.407 |
-2.577 |
-1.166 |
-1.108 |
-2.148 |
-1.967 |
0.8482665 |
0.053 |
| slr1974 |
slr1974 |
-1.295 |
-0.202 |
0.509 |
0.64 |
-2.153 |
-0.861 |
0.7460569 |
0.127 |
| glcP |
sll0771 |
0.113 |
-0.381 |
-0.143 |
0.008 |
-2.164 |
-1.027 |
0.9982143 |
0.000 |
| minE |
ssl0546 |
0.975 |
-1.768 |
-1.032 |
-0.849 |
-2.194 |
0.18 |
0.8244200 |
0.032 |
| purA |
sll1823 |
-1.4 |
-0.755 |
1.149 |
0.416 |
-2.4 |
-0.727 |
0.6987707 |
0.151 |
| glgA |
sll1393 |
-0.137 |
-0.226 |
0.031 |
-0.204 |
-2.7 |
1.505 |
0.9275690 |
0.004 |
| pilT |
sll1533 |
-1.207 |
-0.456 |
-0.979 |
1.343 |
-2.71 |
0.646 |
0.7070054 |
0.138 |
| ycf19 |
ssr2142 |
1.071 |
-3.914 |
-0.278 |
0.284 |
-2.735 |
1.742 |
0.5878787 |
0.226 |
| ssl3364 |
ssl3364 |
0.214 |
-1.289 |
-0.623 |
-0.271 |
-3.033 |
-0.61 |
0.9116291 |
0.010 |
| glk |
sll0593 |
-0.008 |
-0.723 |
-0.614 |
-0.394 |
-3.701 |
-2.08 |
0.9860610 |
0.000 |
df_linreg_wide %>% color_table("+D")
| sgRNA_target |
locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
pval_+D |
| psbB |
slr0906 |
-0.064 |
1.707 |
0.622 |
-0.176 |
1.44 |
4.62 |
0.8709903 |
0.028 |
| psbE |
ssr3451 |
0.191 |
1.559 |
0.07 |
0.163 |
1.443 |
3.846 |
0.9215596 |
0.012 |
| psbC |
sll0851 |
-0.001 |
1.691 |
0.24 |
0.281 |
3.288 |
3.75 |
0.9364716 |
0.030 |
| psbF |
smr0006 |
0.201 |
1.328 |
0.045 |
-0.021 |
1.078 |
3.584 |
0.9289141 |
0.009 |
| sll0364 |
sll0364 |
2.834 |
-2.812 |
-0.108 |
1.758 |
0.601 |
3.578 |
0.8717770 |
0.072 |
| rpoDI |
slr0653 |
1.855 |
-0.679 |
-1.12 |
-1.39 |
-1.275 |
3.412 |
0.5935773 |
0.155 |
| psbD2 |
slr0927 |
-0.005 |
1.286 |
0.228 |
-0.241 |
1.923 |
3.353 |
0.9093335 |
0.031 |
| psbJ |
smr0008 |
0.192 |
2.968 |
0.293 |
0.419 |
3.216 |
3.222 |
0.8923081 |
0.079 |
| psbD |
sll0849 |
-0.861 |
2.664 |
1.002 |
-0.005 |
3.231 |
3.101 |
0.8557861 |
0.152 |
| ndhF |
slr2009 |
0.221 |
2.237 |
0.905 |
0.613 |
2.258 |
3.08 |
0.9355070 |
0.018 |
| slr1505 |
slr1505 |
-0.682 |
2.926 |
0.456 |
0.864 |
2.222 |
3.068 |
0.9263147 |
0.031 |
| tktA |
sll1070 |
0.198 |
0.59 |
0.503 |
0.822 |
1.981 |
2.992 |
0.9639206 |
0.006 |
| apcA |
slr2067 |
0.027 |
4.441 |
0.576 |
0.809 |
3.301 |
2.922 |
0.7380667 |
0.289 |
| lysA |
sll0504 |
-0.206 |
0.251 |
0.394 |
0.088 |
0.396 |
2.767 |
0.9715674 |
0.001 |
| cpcB |
sll1577 |
0.12 |
4.032 |
0.141 |
0.74 |
2.544 |
2.476 |
0.8276584 |
0.181 |
| slr1990 |
slr1990 |
-0.768 |
3.219 |
0.197 |
0.994 |
2.472 |
2.429 |
0.8920252 |
0.102 |
| psbL |
smr0007 |
0.139 |
1.635 |
0.417 |
0.57 |
1.385 |
2.402 |
0.8022242 |
0.083 |
| apcB |
slr1986 |
0.627 |
2.876 |
0.319 |
0.432 |
2.241 |
2.188 |
0.7619664 |
0.232 |
| slr0734 |
slr0734 |
0.52 |
2.819 |
0.607 |
0.753 |
1.99 |
2.183 |
0.8211958 |
0.134 |
| hemA |
slr1808 |
0.905 |
1.077 |
-0.68 |
-0.639 |
1.149 |
2.174 |
0.5761540 |
0.327 |
| cpcA |
sll1578 |
0.11 |
3.57 |
0.261 |
0.609 |
2.255 |
2.162 |
0.8434275 |
0.164 |
| ftsZ |
sll1633 |
2.34 |
-2.314 |
-1.677 |
-1.439 |
0.318 |
2.136 |
0.9249829 |
0.054 |
| sll6055 |
sll6055 |
-1.122 |
3.123 |
0.558 |
0.514 |
2.406 |
2.128 |
0.8376624 |
0.217 |
| sll0930 |
sll0930 |
0.084 |
0.116 |
0.329 |
0.368 |
1.209 |
2.055 |
0.8790537 |
0.046 |
| cbbA |
sll0018 |
-0.644 |
1.164 |
0.415 |
0.545 |
2.607 |
2.054 |
0.9537621 |
0.043 |
| sll0983 |
sll0983 |
1.392 |
-1.936 |
0.075 |
-0.306 |
-0.69 |
2.036 |
0.8763437 |
0.041 |
| glk |
sll0593 |
-0.008 |
-0.723 |
-0.614 |
-0.394 |
-3.701 |
-2.08 |
0.9860610 |
0.012 |
| ndhC |
slr1279 |
0.96 |
-0.173 |
0.393 |
-0.18 |
-0.985 |
-2.147 |
0.8635036 |
0.098 |
| ycf43 |
sll0194 |
-0.722 |
-0.119 |
0.025 |
0.102 |
1.626 |
-2.206 |
0.9735251 |
0.001 |
| sll1496 |
sll1496 |
1.499 |
-1.248 |
-0.507 |
-0.657 |
3.237 |
-2.22 |
0.9150619 |
0.088 |
| pmgA |
sll1968 |
1.162 |
-1.905 |
0.387 |
-1.343 |
-1.769 |
-2.242 |
0.5849242 |
0.429 |
| ssr2333 |
ssr2333 |
0.305 |
-1.552 |
-0.003 |
-0.034 |
0.305 |
-2.319 |
0.4255312 |
0.285 |
| ssl0438 |
ssl0438 |
0.12 |
-1.474 |
-0.18 |
-0.419 |
-0.219 |
-2.375 |
0.8654067 |
0.019 |
| atpI |
sll1322 |
-1.547 |
-0.422 |
1.294 |
0.662 |
0.252 |
-2.475 |
0.4121488 |
0.353 |
| slr1098 |
slr1098 |
0.903 |
-2.261 |
-0.199 |
-0.434 |
0.479 |
-2.543 |
0.4195152 |
0.367 |
| talB |
slr1793 |
0.152 |
-0.144 |
-0.806 |
-0.023 |
0.111 |
-2.879 |
0.9813772 |
0.000 |
| petH |
slr1643 |
-1.23 |
2.351 |
1.048 |
1.592 |
2.039 |
-2.97 |
0.7531722 |
0.066 |
| zwf |
slr1843 |
-0.079 |
0.297 |
-0.149 |
0.054 |
-0.043 |
-2.998 |
0.9966699 |
0.000 |
| slr1245 |
slr1245 |
-2.694 |
1.826 |
1.245 |
-0.805 |
1.092 |
-3.224 |
0.8325233 |
0.067 |
Based on the multiple linear model correlations, we can try to extract a shortlist of the most interesting hypothetical genes. These could warrant further investigations.
list_top_unknown_hits <- df_linreg_wide %>%
left_join(df_uniprot, by = "locus") %>%
# filter by name: only unknown proteins
filter(
is.na(gene_name_short),
str_detect(protein, "[a-zA-Z]{3}[0-9]{4} protein|Uncharacterized")) %>%
# filter by effect: only correlation > 3
filter(if_any(matches("^(carb|light|\\-|\\+)"), ~ abs(.) > 3)) %>%
arrange(desc(r_squared)) %>%
pull(locus)
df_linreg_wide %>% filter(locus %in% list_top_unknown_hits) %>%
select(!starts_with("pval"), -sgRNA_target) %>%
mutate(across(2:7, ~ cell_spec(., "html", color = "white",
background = spec_color(., option = "E", scale = c(-5.5, 5.5)),
bold = TRUE))) %>%
kbl(format = "html", escape = F) %>%
kable_paper("striped", full_width = F)
| locus |
carbon |
light |
-N |
+FL |
+G |
+D |
r_squared |
| sll0364 |
2.834 |
-2.812 |
-0.108 |
1.758 |
0.601 |
3.578 |
0.8717770 |
| sll0481 |
2.141 |
-3.371 |
-1.601 |
-1.335 |
0.485 |
1.056 |
0.9686290 |
| sll0877 |
1.76 |
-3.694 |
-2 |
-1.39 |
-1.273 |
0.847 |
0.7839636 |
| sll1378 |
-0.955 |
3.701 |
-0.4 |
1.2 |
2.782 |
0.937 |
0.9545533 |
| sll6055 |
-1.122 |
3.123 |
0.558 |
0.514 |
2.406 |
2.128 |
0.8376624 |
| slr1102 |
0.056 |
3.162 |
-0.118 |
0.511 |
2.737 |
0.819 |
0.8207709 |
| slr1505 |
-0.682 |
2.926 |
0.456 |
0.864 |
2.222 |
3.068 |
0.9263147 |
| slr1990 |
-0.768 |
3.219 |
0.197 |
0.994 |
2.472 |
2.429 |
0.8920252 |
| ssl3364 |
0.214 |
-1.289 |
-0.623 |
-0.271 |
-3.033 |
-0.61 |
0.9116291 |
| ssr3532 |
2.303 |
-1.661 |
-3.733 |
-1.464 |
0.702 |
1.64 |
0.6071408 |
Extract and analyze interesting gene clusters
The list above shows the genes whose fitness is most significantly correlated with one of the treatments. This list of genes is extracted and then simply fitness per condition is plotted as a heatmap, in order to confirm the trends from fitting the multiple liner regression models.
plot_sgRNAs_light <- df_gene %>%
filter(locus %in% list_top_unknown_hits, time == 0) %>%
mutate(sgRNA_target = fct_cluster(sgRNA_target, condition, wmean_fitness)) %>%
mutate(condition = fct_cluster(condition, sgRNA_target, wmean_fitness)) %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
ggplot(aes(x = condition, y = sgRNA_target, fill = wmean_fitness)) +
geom_tile() + custom_theme() +
labs(title = "Top unknown genes", x = "", y = "") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
print(plot_sgRNAs_light)

save_plot(plot_sgRNAs_light, width = 8.0, height = 3.5)
Summary
sll0364 - 139 AA. KD has higher fitness in HC conditions and lower fitness in HL. Negatively regulating carbon metabolism?
sll0481 - 155 AA. KD has higher fitness in +G conditions and lower fitness in HL. Membrane localization. Negatively regulating glycolysis?
sll0877 - 456 AA. KD has higher fitness only in HC,LL. Mitigates light limitation?
ssl3364 - 74 AA. KD has lower fitness on all HC/+G conditions. This protein is known as CP12 protein, regulating glycolytic flux at GAPDH and PRK.
ssr3532 - 80 AA. KD lower fitness on N-limitation and C-limitation (LC-HL combinations). Same operon as glutaminase glsA (slr2079, catalyzes deamination of gln –> glu), regulatory, involved in N metabolism?
slr1990 - 240 AA, 5 TM domains. KD higher fitness in photoheterotrophy, lower fitness in all HC/LL conditions. Something important for photosystems? Something that wastes e- in photoheterotrophic conditions?
sll6055 - 152 AA. Fitness profile as above. Multiubiquitin domain, involved in protein modification/degradation of PS proteins?
slr1505 - 198 AA. Fitness profile as above. No useful information.
sll1378 - 300 AA. KD has lower fitness on all LL conditions. Membrane associated protein? In STRING, potential interaction with PbsA1 and PbsA2 (Heme oxygenase 1 and 2). Potentially important for chlorophyll or heme biosynthesis –> would explain importance for photosynthesis in LL condition.
slr1102 - 853 AA. KD has lower fitness on all LL conditions. 4 known domains, FHA (forkhead-associated domain is a phosphopeptide recognition domain found in many regulatory proteins), PAS (signaling, often involved in circadian proteins, detect their signal by way of an associated cofactor like heme, flavin), GGDEF (involved in signal transduction, likely to catalyze synthesis or hydrolysis of cyclic diguanylate c-diGMP), EAL (shown to stimulate degradation of a second messenger, cyclic di-GMP, candidate for a diguanylate phosphodiesterase function. Together with the GGDEF domain, EAL might be involved in regulating cell surface adhesiveness in bacteria). Source: InterPro. Embedded in a tight network of interacting proteins all involved in chromophore biosynthesis/maturation.
Apc and cpc repression mutants encoding phycobilisomes are also enriched in high light
plot_sgRNAs_phycobil <- df_gene %>%
filter(str_detect(gene_name, "[ac]pc"), time == 0) %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
ggplot(aes(x = condition, y = fct_rev(sgRNA_target), fill = wmean_fitness)) +
geom_tile() + custom_theme() +
labs(title = "Apc/Cpc repression mutants", x = "", y = "") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
print(plot_sgRNAs_phycobil)

save_plot(plot_sgRNAs_phycobil, width = 6.5, height = 3.5)
Differential fitness of selected gene sets
Central carbon metabolism
To plot gene fitness for the enzymes of central carbon metabolism, we use the complete list of enzymes and the genes that they are mapped to (obtained from KEGG). We can extract gene sets for specific pathways and plot fitness. We start with glycolysis and Calvin cycle enzymes.
list_central_met_pathways <- c(
"Glycolysis / Gluconeogenesis",
"Pentose phosphate pathway",
"Carbon fixation in photosynthetic organisms",
"Photosynthesis",
"Citrate cycle (TCA cycle)",
"Pyruvate metabolism",
"Glyoxylate and dicarboxylate metabolism"
)
plot_gene_fitness <- function(df, pw = NULL, gene = NULL,
title = NULL, ncol = 8, legend.position = "bottom") {
df <- df %>% filter(time == 0)
if (!is.null(pw)) {
df <- df %>% inner_join(df_kegg %>% filter(kegg_pathway == pw) %>% select(locus),
by = "locus")
title <- pw
} else if (!is.null(gene)) {
df <- df %>% filter(locus %in% gene)
}
ggplot(df, aes(x = condition, y = wmean_fitness,
ymin = wmean_fitness-sd_fitness,
ymax = wmean_fitness+sd_fitness, fill = condition, color = condition)) +
geom_col(position = "dodge", width = 0.6) +
geom_errorbar(position = "dodge", width = 0.6, size = 1) +
custom_theme(aspect.ratio = 1,
legend.position = legend.position, legend.key.size = unit(0.4, "cm")) +
labs(title = title, x = "", y = "fitness") +
theme(axis.text.x = element_blank(), axis.ticks = element_blank()) +
scale_fill_manual(values = colorRampPalette(custom_colors[1:5])(11)) +
scale_color_manual(values = colorRampPalette(custom_colors[1:5])(11)) +
facet_wrap(~ sgRNA_target, ncol = ncol, drop = FALSE)
}
print(plot_gene_fitness(df_gene, pw = list_central_met_pathways[[1]]))

ggsave("../figures/plot_fitness_glycolysis.svg",
plot_gene_fitness(df_gene, pw = list_central_met_pathways[[1]]),
width = 8, height = 6)
print(plot_gene_fitness(df_gene, pw = list_central_met_pathways[[2]]))

ggsave("../figures/plot_fitness_pentose.svg",
plot_gene_fitness(df_gene, pw = list_central_met_pathways[[2]]),
width = 8, height = 5)
print(plot_gene_fitness(df_gene, pw = list_central_met_pathways[[3]]))

ggsave("../figures/plot_fitness_carbonfix.svg",
plot_gene_fitness(df_gene, pw = list_central_met_pathways[[3]]),
width = 8, height = 5)
print(plot_gene_fitness(df_gene, pw = list_central_met_pathways[[5]]))

ggsave("../figures/plot_fitness_citrate.svg",
plot_gene_fitness(df_gene, pw = list_central_met_pathways[[5]]),
width = 8, height = 4)
Gene fitness in mixotrophy and heterotrophy
Using fluctuator, we can import a custom metabolic map for Synechocystis sp. PCC 6803, and overlay published fluxes that were measured with LC-MS using isotopically labelled carbon sources (Nakajima et al., 2014).
Fluctuator can be installed using a function from devtools:
devtools::install_github("m-jahn/fluctuator")
We import the metabolic flux data from the supplemental items of Nakajima et al., 2014.
library(fluctuator)
# import flux data
df_nakajima_mfa <- read.csv("../data/input/Nakajima2014_metabolic_fluxes.csv")
# generate stroke width and color
df_nakajima_mfa <- df_nakajima_mfa %>%
mutate(
stroke_width = 0.3 + (0.7*sqrt(abs(flux))),
stroke_color = abs(flux) %>% {1+(./max(.))*9} %>% round,
stroke_color_rgb = colorRampPalette(custom_colors[c(5,2,1)])(10)[stroke_color])
The next step is to overlay the fluxes. We generate two types of maps, mixotrophy and photoheterotrophy. The stroke width and color for all reactions is set by the flux magnitude.
for (cond in c("mixotroph", "photoheterotroph")) {
# import map
SVG_template <- read_svg("../data/input/map_central_metabolism_syn.svg")
# set stroke on SVG map
SVG_mix <- set_attributes(SVG_template,
node = filter(df_nakajima_mfa, condition == cond)$reaction,
attr = "style",
pattern = "stroke-width:[0-9]+\\.[0-9]+",
replacement = paste0("stroke-width:",
filter(df_nakajima_mfa, condition == cond)$stroke_width))
# set color
SVG_mix <- set_attributes(SVG_mix,
node = filter(df_nakajima_mfa, condition == cond)$reaction,
attr = "style",
pattern = "stroke:#b3b3b3",
replacement = paste0("stroke:",
filter(df_nakajima_mfa, condition == cond)$stroke_color_rgb))
# set arrow directionality
SVG_mix <- set_attributes(SVG_mix,
node = filter(df_nakajima_mfa, condition == cond, flux < 0)$reaction,
attr = "style",
pattern = "marker-end:url\\(#marker[0-9]*\\);",
replacement = "")
SVG_mix <- set_attributes(SVG_mix,
node = filter(df_nakajima_mfa, condition == cond, flux > 0)$reaction,
attr = "style",
pattern = "marker-start:url\\(#marker[0-9]*\\);",
replacement = "")
write_svg(SVG_mix, file = paste0("../data/output/map_", cond, "y.svg"))
}
 |
 |
Now we plot fitness of central carbon metabolism genes for two or three selected conditions. These will be added to the metabolic map manually. The mixotrophic conditions LC, LL, +G and HC, LL, +G turned out to be very similar.
df_centralcarb <- tibble(
locus = c( "sll0593", "slr0329", "slr1843", "sll1479", "sll0329", "slr1349",
"slr0952", "slr2094", "slr0943", "sll0018", "slr0783", "sll1342", "slr0884",
"slr0394", "slr1945", "slr0752", "sll0587", "sll1275", "sll1070", "slr1793",
"slr0194", "ssl2153", "sll0807", "sll1525", "slr0009", "slr0012", "sll1721",
"sll1841", "slr1096", "slr1934", "sll0401", "slr0665", "slr1289", "slr1096",
"sll1023", "sll1557", "sll0823", "sll1625", "slr0201", "slr1233", "slr0018",
"sll0891", "sll0920", "slr0721"),
reaction = c("HEX", "HEX", "G6PDH", "PGL", "GND", "PGI", "FBP",
"FBP", "FBA", "FBA", "TPI", "GAPDH", "GAPDH", "PGK", "PGM", "ENO",
"PYK", "PYK", "TKT", "TAL", "RPI", "RPI", "RPE", "PRUK", "RUBISCO",
"RUBISCO", "PDH", "PDH", "PDH", "PDH", "CS", "ACONT", "ICDH", "AKGDH",
"SUCOAS", "SUCOAS", "SUCD", "SUCD", "SUCD", "SUCD", "FUM", "MDH",
"PPC", "ME")) %>%
inner_join(df_kegg) %>% group_by(locus) %>% slice(1) %>%
ungroup %>% arrange(reaction)
Joining, by = "locus"
plot_centralcarb_minifig <- df_gene %>% filter(
time == 0,
condition %in% c("LC, LL", "LC, LL, +G", "LC, LL, +D, +G")) %>%
inner_join(df_centralcarb, by = "locus") %>%
mutate(sgRNA_target = paste0(reaction, " (", sgRNA_target, ")")) %>%
mutate(condition = factor(condition, c("LC, LL", "LC, LL, +G", "LC, LL, +D, +G"))) %>%
ggplot(aes(x = condition, y = wmean_fitness,
ymin = wmean_fitness-sd_fitness,
ymax = wmean_fitness+sd_fitness, fill = condition, color = condition)) +
geom_hline(yintercept = c(0, -5, -10), linetype = 3, col = grey(0.6)) +
geom_col(position = "dodge", width = 0.6) +
geom_errorbar(position = "dodge", width = 0.6, size = 1) +
custom_theme(aspect.ratio = 1,
legend.position = "bottom", legend.key.size = unit(0.4, "cm")) +
theme(axis.text.x = element_blank(), axis.text.y = element_blank(),
axis.ticks = element_blank(), panel.grid.major = element_blank(),
strip.text = element_text(size = 8)) +
labs(x = "", y = "") +
coord_cartesian(ylim = c(-11, 1)) +
scale_fill_manual(values = custom_colors[c(5,2,3)]) +
scale_color_manual(values = custom_colors[c(5,2,3)]) +
facet_wrap(~ sgRNA_target, ncol = 9, drop = FALSE)
plot_centralcarb_minifig

Similar but more concisely, we can test if there is a correaltion between flux and fitness penalty upon repression. Theoretically, such a correlation should exist because repression of high flux enzymes should have the strongest penalty on fitness. However, examination of the data does not reveal a clear correlation. Causes for this may be manifold, including the compensation of gene duplicates/iso-enzymes for gene knock down at high flux reactions.
df_gene %>% filter(
time == 0,
condition %in% c("LC, LL, +G", "LC, LL, +D, +G")) %>%
mutate(condition = recode(condition,
`LC, LL, +G` = "mixotroph", `LC, LL, +D, +G` = "photoheterotroph")) %>%
inner_join(df_centralcarb, by = "locus") %>%
select(sgRNA_target, locus, gene_name, condition, wmean_fitness, sd_fitness, reaction) %>%
inner_join(by = c("condition", "reaction"),
select(df_nakajima_mfa, condition, reaction, flux, ci_low, ci_high)) %>%
ggplot(aes(x = abs(flux), y = abs(wmean_fitness),
ymin = abs(wmean_fitness)-sd_fitness,
ymax = abs(wmean_fitness)+sd_fitness,
xmin = abs(ci_low),
xmax = abs(ci_high))) +
geom_errorbar(orientation = "x", col = grey(0.75)) +
geom_errorbar(orientation = "y", col = grey(0.75)) +
geom_point() +
coord_cartesian(xlim = c(-0.2, 3.2), ylim = c(-0.5, 7.5)) +
custom_theme(aspect.ratio = 1) +
facet_wrap(~ condition, ncol = 2)

Adaptation to light and carbon excess
We will look at three different types of regulatory adaptations:
apc/cpcantenna proteins (phycobilisomes), known to be among the most expressed and regulated genes in cyanos
- flavoproteins Flv1 (
sll1521), Flv2 (sll0219), Flv3 (sll0550), Flv4 (sll0217), sll0218 (in flv2/4 operon)
- low affinity/high flux transporters Ci transporters: bicA (
sll0834), NDH-I4 with ndhF4, D4, cupB (sll0026, sll0027, slr1302)
- high affinity/low flux inducible Ci transporters: BCT1/cmpAB(porB)CD (
slr0040-44), SbtA/B (slr1512, slr1513), NDH-I3 with ndhF3, ndhD3, cupA, cupS (sll1732-35)
- carbon transport regulatory proteins: ccmR/rbcR (
sll1594), cmpR (sll0030), cyabrB1 (sll0359), cyabrB2 (sll0822)
plot_phycobilisome <- df_gene %>% filter(str_detect(gene_name, "[ac]pc[ABCDEFG]")) %>%
plot_gene_fitness(ncol = 6, legend.position = 0)
plot_flv_genes <- df_gene %>% filter(locus %in% c("sll1521", "sll0219", "sll0550", "sll0217", "sll0218")) %>%
mutate(sgRNA_target = recode(sgRNA_target, `sll1521` = "Flv1 (sll1521)", `sll0219` = "Flv2 (sll0219)",
`sll0550` = "Flv3 (sll0550)", `sll0217` = "Flv4 (sll0217)")) %>%
mutate(sgRNA_target = factor(sgRNA_target, c(unique(sgRNA_target), ""))) %>%
plot_gene_fitness(ncol = 6, legend.position = 0)
plot_carbon_uptake <- df_gene %>% filter(locus %in% c(
"sll0026", "sll0027", "slr1302",
"sll1732", "sll1733", "sll1734", "sll1735", "slr0040", "slr0041","slr0043","slr0044"
)) %>%
mutate(sgRNA_target = recode(sgRNA_target,
`nrtC2` = "cmpC", `nrtD3` = "cmpD",
`sll1734` = "cupA", `slr1302` = "cupB",
`sll1735` = "cupS", `ndhF2` = "ndhF3"
)) %>%
mutate(sgRNA_target = factor(sgRNA_target, unique(sgRNA_target)[c(4,6,11,3,5,9,10,1,2,7,8)])) %>%
plot_gene_fitness(ncol = 6, legend.position = 0) +
coord_cartesian(ylim = c(-7.9, 2.4))
Figure 3 draft:
ggarrange(nrow = 3, heights = c(0.47, 0.2, 0.33), labels = LETTERS[1:3], font.label = list_fontpars,
plot_phycobilisome,
plot_flv_genes,
plot_carbon_uptake
)

As a Supplementary figure to C), we can plot all other carbon transporters and regulatory genes that showed a less remarkable effect.
plot_carbon_uptake_2 <- df_gene %>% filter(locus %in% c(
"sll0834", "slr1512", "slr1513", "sll1594", "sll0030", "sll0359", "sll0822"
)) %>%
mutate(sgRNA_target = recode(sgRNA_target,
`sll0834` = "bicA", `slr1512` = "sbtA", `slr1513` = "sbtB",
`sll0359` = "cyabrB1", `sll0822` = "cyabrB2", `rbcR` = "ccmR"
)) %>%
mutate(sgRNA_target = factor(sgRNA_target, unique(sgRNA_target))) %>%
plot_gene_fitness(ncol = 4, legend.position = "right")
plot_carbon_uptake_2

As another Supplementary Figure, we can plot the total protein mass of the phycobilisome determined by protein mass spectrometry. This data was published in our study Jahn et al., Cell Reports, 2018. The data can be downloaded directly from the ShinyProt github page where it is included for on demand visualization.
load(url("https://github.com/m-jahn/ShinyProt/blob/master/data/Jahn_2018_Light_and_CO2_lim.Rdata?raw=true"))
plot_protmass_phycobilisome1 <- Jahn_2018_Light_and_CO2_lim %>%
filter(str_detect(protein, "[ac]pc[ABCDEFG]"), sample != "CO2") %>%
mutate(protein = str_extract(protein, "[ac]pc[ABCDEFG][12]?")) %>%
ggplot(aes(x = factor(light), y = 100*mean_mass_fraction_norm,
fill = str_sub(protein, 1, 3), label = protein)) +
lims(y = c(0, 22)) +
geom_col(position = "stack", width = 0.7, col = grey(1), size = 0.2) +
geom_text(size = 2.5, position = position_stack(vjust = 0.5), color = "white") +
custom_theme(legend.position = "bottom", legend.key.size = unit(0.5, "cm")) +
labs(title = "Light limitation", x = "µmol photons m^-2 s^-1", y = "% protein mass") +
scale_fill_manual(values = custom_colors[c(2,4)]) +
scale_color_manual(values = custom_colors[c(2,4)])
plot_protmass_phycobilisome2 <- Jahn_2018_Light_and_CO2_lim %>%
filter(str_detect(protein, "[ac]pc[ABCDEFG]"), sample == "CO2") %>%
mutate(protein = str_extract(protein, "[ac]pc[ABCDEFG][12]?")) %>%
ggplot(aes(x = factor(co2_concentration), y = 100*mean_mass_fraction_norm,
fill = str_sub(protein, 1, 3), label = protein)) +
lims(y = c(0, 22)) +
geom_col(position = "stack", width = 0.7, col = grey(1), size = 0.2) +
geom_text(size = 2.5, position = position_stack(vjust = 0.5), color = "white") +
custom_theme(legend.position = "bottom", legend.key.size = unit(0.5, "cm")) +
labs(title = "CO2 limitation", x = "% CO2 in air", y = "% protein mass") +
scale_fill_manual(values = custom_colors[c(2,4)]) +
scale_color_manual(values = custom_colors[c(2,4)])
ggarrange(ncol = 2, widths = c(0.5,0.5),
labels = LETTERS[1:2], font.label = list_fontpars,
plot_protmass_phycobilisome1,
plot_protmass_phycobilisome2
)

Other genes of interest that either did not show any (remarkable) effect on fitness, or do not meet the scope of this section:
- OCP (
slr1963), pgr5 (ssr2016)
- SigB (
sll0306), SigC (sll0184), SigD (sll2012), SigE (sll1689) (rpoD genes 1-4)
- ccmM (
sll1031), ccmK2 (sll1028), ccmK1 (sll1029), ccmN (sll1032), ccmO (slr0436), ccmL (sll1030)
- CP12 (
ssl3364)
Genes where knock down leads to increased fitness
list_genes_pos_fitness <- df_gene %>%
filter(time == 0, !is.na(locus), wmean_fitness > 2) %>%
pull(locus) %>% unique
plot_gene_fitness(df_gene, gene = list_genes_pos_fitness, title = "Genes with increased fitness (f > 2)")

ggsave("../figures/plot_fitness_increased.svg",
plot_gene_fitness(df_gene, gene = list_genes_pos_fitness, title = "Genes with increased fitness (f > 2)"),
width = 8, height = 8)
Summary: - pmgA is once again the gene with strongest and most widespread fitness increase, validating results from library V1 - slr1916 same phenotype as pmgA just weaker. We also know this one from before. Must have identical role as pmgA. - all PSII genes show increased fitness in photoheterotrophic condition –> PS is a burden here - sll0689, pxcA, slr1609 - all increased fitness in HC,HL, first two are Na+/CO2 (?) trnasporters, slr1609 we know from before, annotated as fatty acid CoA ligase, but probably it’s something different - sll6055, slr1505, slr1990 - all increased fitness in photoheterotrophic condition, and decreased fitness in HC/LL conditions. Not much is known about these genes, probably a role in photosynthesis, as the pattern is similar to psb genes (PSII maturation?) - slr0813, slr0907, slr909, slr1299 - all increased fitness in HC/LL. Not clear what connects these genes functionally.
Differential fitness of non-coding RNAs (ncRNAs)
General trends
The first task to study ncRNAs is to generate a new data frame with additional annotation for ncRNAs. Additional annotation tables were exported from Geneious and are based on the publication from Mitschke et al., PNAS, 2010. According to this publication, ncRNAs are grouped into four different (slightly overlapping) classes:
- non-coding regulatory RNAs (ncRNAs in strict sense) not associated to a gene
- iTSS, internal TSS within a gene
- asRNA, regulatory anti-sense RNAs associated with a gene
- Not included: 5’UTRs, alternative transcription start sites (TSS) associated to a gene
df_ncRNA <- df_main %>% filter(sgRNA_type == "ncRNA") %>%
# obtain number of sgRNAs per target
group_by(sgRNA_target) %>%
summarize(sgRNA_number = length(unique(sgRNA_position))) %>%
# merge with df_gene table
inner_join(df_gene, by = "sgRNA_target") %>%
# generate ncRNA type based on target name
select(-locus, -gene_name, -sgRNA_type) %>%
mutate(sgRNA_target = str_sub(sgRNA_target, 4, 1000)) %>%
left_join(by = "sgRNA_target",
bind_rows(lapply(c("NC_000911_ncRNA.tsv", "NC_000911_asRNA.tsv", "NC_000911_iTSS.tsv"),
FUN = function(f) read_tsv(paste0("../data/input/", f), col_types = cols())
))
)
df_ncRNA %>% group_by(ncRNA_type) %>%
filter(time == 0, condition == "HC, HL") %>%
select(sgRNA_target, score) %>%
summarize(n_targets = length(unique(sgRNA_target)), sum(score >= 4), sum(score < 4),
percent = sum(score >= 4)/n()*100)
Adding missing grouping variables: `ncRNA_type`
Looking at the fitness and significance scores for one conditions, it seems as internal transcription start sites are overrepresented in the group that shows an effect. This is not a surprise, given that sgRNAs targeting iTSS basically also repress the native gene as a regular sgRNA. We therefore exlcude iTSS from the analysis.
The library contains 1712 ncRNAs each targeted by 1 to 5 sgRNAs. Only very few of those showed an effect on fitness. We can filter all ncRNAs that have a “significance” equivalent to a fitness score abs(F) >= 2 and -log10 p-value >= 2 (alpha = 0.01). Significance here means effect size (F) multiplied by -log10 p-value, the threshold is indicated by the dashed line.
plot_ncRNA_overview <- df_ncRNA %>% filter(time == 0, condition == "HC, HL") %>%
mutate(ncRNA_type = factor(ncRNA_type, c("asRNA", "ncRNA", "iTSS"))) %>%
ggplot(aes(x = wmean_fitness, y = -log10(p_value_adj), color = ncRNA_type)) +
geom_point(alpha = 0.5, size = 1.5) +
geom_line(data = data.frame(x = c(seq(-8, -0.5, 0.1), seq(0.5, 8, 0.1)),
y = 4/c(seq(8, 0.5, -0.1), seq(0.5, 8, 0.1))),
aes(x = x, y = y, shape = NULL, col = NULL), lty = 2) +
coord_cartesian(xlim = c(-7, 7), ylim = c(0, 4)) +
custom_theme(aspect = 1, legend.position = "none") +
facet_wrap(~ ncRNA_type, ncol = 1) +
labs(x = "fitness", y = expression("-log"[10]*" p-value")) +
scale_color_manual(values = custom_colors)
print(plot_ncRNA_overview)

Antisense RNAs as regulatory elements
The first part of a more detailed analysis is to extract asRNAs with differential fitness, and compare them to their associated genes. The assumption is that sgRNAs targeting asRNAs in reality repress transcription of their parent genes, and by these means produce a fitness effect that can not be attributed to the action of the asRNA itself. The first step is filter the ncRNA dataset and order ncRNAs by fitness similarity.
df_ncRNA_select <- df_ncRNA %>%
filter(ncRNA_type != "iTSS", time == 0) %>%
group_by(sgRNA_target) %>%
filter(any(score >= 4)) %>% ungroup %>%
mutate(sgRNA_target = fct_cluster(sgRNA_target, condition, wmean_fitness))
plot_asRNA_heat <- df_ncRNA_select %>% filter(ncRNA_type == "asRNA") %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
ggplot(aes(x = condition, y = sgRNA_target, fill = wmean_fitness)) +
geom_tile() + custom_theme(legend.pos = "bottom") +
labs(x = "", y = "") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
# check correlation of asRNA fitness with associated gene fitness
plot_asRNA_xy <- df_ncRNA_select %>% filter(ncRNA_type == "asRNA") %>%
left_join(by = c("condition", "locus"),
select(df_gene, locus, condition, wmean_fitness, sd_fitness) %>% distinct %>%
rename(gene_fitness = wmean_fitness, sd_gene_fitness = sd_fitness)) %>%
select(locus, condition, wmean_fitness, gene_fitness) %>%
mutate(locus = if_else(locus %in% c("slr0882", "sll1773", "sml0004", "slr1609", "slr1939"), locus, "other")) %>%
ggplot(aes(x = wmean_fitness, y = gene_fitness, color = locus)) +
geom_abline(intercept = 0, slope = 1, lty = 2) +
geom_abline(intercept = 4, slope = 1, lty = 2) +
geom_abline(intercept = -4, slope = 1, lty = 2) +
geom_point() +
coord_cartesian(xlim = c(-9, 5), ylim = c(-9, 5)) +
custom_theme(legend.pos = c(0.15, 0.8), legend.key.size = unit(0.3, "cm")) +
labs(x = "asRNA fitness", y = "gene fitness") +
scale_color_manual(values = custom_colors[c(5,1:4,6)])
noncoding RNAs as regulatory elements
The second part of this analysis is to look at non-gene associated (intergenic) ncRNA elements. Of these, several are known to have a regulatory effect.
plot_ncRNA_heat <- df_ncRNA_select %>% filter(ncRNA_type == "ncRNA") %>%
mutate(wmean_fitness = wmean_fitness %>% replace(., . > 4, 4) %>% replace(., . < -4, -4)) %>%
ggplot(aes(x = condition, y = sgRNA_target, fill = wmean_fitness)) +
geom_tile() + custom_theme(legend.pos = "none") +
labs(x = "", y = "") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_fill_gradientn(colours = c(custom_colors[1], grey(0.9), custom_colors[2]),
limits = c(-4, 4))
ggarrange(ncol = 3,
plot_ncRNA_overview,
ggarrange(nrow = 2, heights = c(0.65, 0.35),
plot_asRNA_heat, plot_asRNA_xy + theme(plot.margin = unit(c(4,12,16,12), "points"))),
ggarrange(nrow = 2, heights = c(0.75, 0.25), plot_ncRNA_heat, ggplot() + custom_theme())
)

LS0tCnRpdGxlOiAiQ1JJU1BSaSBsaWJyYXJ5IFYyLCBkYXRhIHByb2Nlc3NpbmcgcGlwZWxpbmUiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCi0tLS0tLS0tLS0KCiMgRGVzY3JpcHRpb24KClRoaXMgUiBub3RlYm9vayBkZXRhaWxzIHRoZSBkYXRhIHByb2Nlc3NpbmcgYW5kIHZpc3VhbGl6YXRpb24gZm9yIGdyb3d0aCBjb21wZXRpdGlvbiBleHBlcmltZW50cyB3aXRoIGEgQ1JJU1BSaSBzZ1JOQSBsaWJyYXJ5LiBUaGUgbGlicmFyeSBjb250YWlucyBhcm91bmQgMjAsMDAwIHVuaXF1ZSBzZ1JOQSByZXByZXNzaW9uIG11dGFudHMgdGFpbG9yZWQgZm9yIHRoZSBjeWFub2JhY3Rlcml1bSBfU3luZWNob2N5c3Rpc18gc3AuIFBDQzY4MDMuIFRoaXMgbGlicmFyeSBpcyB0aGUgc2Vjb25kIHZlcnNpb24gKHRoZXJlZm9yZSAiVjIiKSBvZiBhbiBzZ1JOQSBsaWJyYXJ5IGZvciBfU3luZWNob2N5c3Rpc18sIGNvbnRhaW5pbmcgZml2ZSBpbnN0ZWFkIG9mIG9ubHkgdHdvIHNnUk5BcyBwZXIgZ2VuZS4gSW4gc29tZSBjYXNlcywgZ2VuZXMgb3IgbmNSTkFzIGFyZSBzbyBzaG9ydCB0aGF0IGl0IGlzIG5vdCBwb3NzaWJsZSB0byBkZXNpZ24gYSBtYXhpbXVtIG9mIGZpdmUgaW5kaXZpZHVhbCBzZ1JOQXMuCgpUaGUgZmlyc3QgaXRlcmF0aW9uIG9mIHRoZSBfU3luZWNob2N5c3Rpc18gc2dSTkEgbGlicmFyeSB3YXMgW3B1Ymxpc2hlZCBpbiBOYXR1cmUgQ29tbXVuaWNhdGlvbnMsIDIwMjBdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNDY3LTAyMC0xNTQ5MS03KS4KCiMgUHJlcmVxdWlzaXRlcwoKTG9hZCByZXF1aXJlZCBwYWNrYWdlcy4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UgfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkoZ2dyZXBlbCkKICBsaWJyYXJ5KGxhdHRpY2UpCiAgbGlicmFyeShsYXR0aWNlRXh0cmEpCiAgbGlicmFyeShsYXR0aWNldG9vbHMpCiAgbGlicmFyeShzY2FsZXMpCiAgbGlicmFyeShkZW5kZXh0ZW5kKQogIGxpYnJhcnkodmVnYW4pCiAgbGlicmFyeSh0c25lKQogIGxpYnJhcnkoS0VHR1JFU1QpCiAgbGlicmFyeShsaW1tYSkKICBsaWJyYXJ5KGNvcnJwbG90KQogIGxpYnJhcnkoa2FibGVFeHRyYSkKICBsaWJyYXJ5KGdyaWQpCiAgbGlicmFyeShnZ3B1YnIpCn0pCmBgYAoKRGVmaW5lIGdsb2JhbCBmaWd1cmUgc3R5bGUsIGRlZmF1bHQgY29sb3JzLCBhbmQgYSBwbG90IHNhdmluZyBmdW5jdGlvbi4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMgY3VzdG9tIGdncGxvdDIgdGhlbWUgdGhhdCBpcyByZXVzZWQgZm9yIGFsbCBsYXRlciBwbG90cwpjdXN0b21fY29sb3JzID0gYygiI0U3Mjk4QSIsICIjNjZBNjFFIiwgIiNFNkFCMDIiLCAiIzc1NzBCMyIsICIjQjNCM0IzIiwgIiMxQjlFNzciLCAiI0Q5NUYwMiIsICIjQTY3NjFEIikKY3VzdG9tX3JhbmdlIDwtIGZ1bmN0aW9uKG4gPSA1KSB7Y29sb3JSYW1wUGFsZXR0ZShjdXN0b21fY29sb3JzW2MoMSw1LDIpXSkobil9CgpjdXN0b21fdGhlbWUgPC0gZnVuY3Rpb24oYmFzZV9zaXplID0gMTIsIGJhc2VfbGluZV9zaXplID0gMS4wLCBiYXNlX3JlY3Rfc2l6ZSA9IDEuMCwgLi4uKSB7CiAgdGhlbWVfbGlnaHQoYmFzZV9zaXplID0gYmFzZV9zaXplLCBiYXNlX2xpbmVfc2l6ZSA9IGJhc2VfbGluZV9zaXplLCBiYXNlX3JlY3Rfc2l6ZSA9IGJhc2VfcmVjdF9zaXplKSArIHRoZW1lKAogICAgdGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gZ3JleSgwLjQpLCBzaXplID0gMTApLAogICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMTIsMTIsMTIsMTIpLCAicG9pbnRzIiksCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gZ3JleSgwLjQpLCBsaW5ldHlwZSA9ICJzb2xpZCIsIGxpbmVlbmQgPSAicm91bmQiKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9IGdyZXkoMC40KSwgc2l6ZSA9IDEwKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9IGdyZXkoMC40KSwgc2l6ZSA9IDEwKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNiwgbGluZXR5cGUgPSAic29saWQiLCBjb2xvdXIgPSBncmV5KDAuOSkpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChsaW5ldHlwZSA9ICJzb2xpZCIsIGNvbG91ciA9IGdyZXkoMC40KSwgZmlsbCA9IE5BLCBzaXplID0gMS4wKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSBncmV5KDAuNCksIHNpemUgPSAxMCwgbWFyZ2luID0gdW5pdChyZXAoMyw0KSwgInBvaW50cyIpKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9IGdyZXkoMC40KSwgc2l6ZSA9IDEwKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgLi4uCiAgKQp9CgojIHNldCBncmFwaGljYWwgcGFyYW1ldGVyIGZvciBzdWJmaWd1cmUgbGFiZWxzCmxpc3RfZm9udHBhcnMgPC0gbGlzdChmYWNlID0gInBsYWluIiwgc2l6ZSA9IDE0KQoKIyBmdW5jdGlvbiB0byBleHBvcnQgYW4gaW1hZ2UgYXMgc3ZnIGFuZCBwbmcKc2F2ZV9wbG90IDwtIGZ1bmN0aW9uKHBsLCBwYXRoID0gIi4uL2ZpZ3VyZXMvIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA2KSB7CiAgcGxfbmFtZSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUocGwpKQogIHN2ZyhmaWxlbmFtZSA9IHBhc3RlMChwYXRoLCBwbF9uYW1lLCAiLnN2ZyIpLAogICAgd2lkdGggPSB3aWR0aCwgaGVpZ2h0ID0gaGVpZ2h0KQogIHByaW50KHBsKQogIGRldi5vZmYoKQogIHBuZyhmaWxlbmFtZSA9IHBhc3RlMChwYXRoLCBwbF9uYW1lLCAiLnBuZyIpLAogICAgd2lkdGggPSB3aWR0aCoxMjUsIGhlaWdodCA9IGhlaWdodCoxMjUsIHJlcyA9IDEyMCkKICBwcmludChwbCkKICBpbnZpc2libGUoY2FwdHVyZS5vdXRwdXQoZGV2Lm9mZigpKSkKfQpgYGAKCgojIFF1YWxpdHkgY29udHJvbAoKIyMgRGF0YSBpbXBvcnQKCkxvYWQgcmF3IGRhdGEuIFRoZSBtYWluIHRhYmxlIGNvbnRhaW5zIGFscmVhZHkgbm9ybWFsaXplZCBxdWFudGlmaWNhdGlvbiBvZiBhbGwgc2dSTkFzLCBmb2xkIGNoYW5nZSwgbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0ZWQgcC12YWx1ZXMsIGFuZCBmaXRuZXNzIHNjb3JlLiBDb250cmFyeSB0byB0aGUgcHJvY2Vzc2luZyBvZiBbb3VyIGZpcnN0IENSSVNQUmkgbGlicmFyeSBWMV0oaHR0cHM6Ly9naXRodWIuY29tL20tamFobi9SLW5vdGVib29rcyksIG11Y2ggb2YgdGhlIGZ1bmN0aW9uYWxpdHkgZnJvbSB0aGUgbm90ZWJvb2sgd2FzIHRyYW5zZmVycmVkIGludG8gdGhlIFtuZXcgQ1JJU1BSaSBsaWJyYXJ5IHBpcGVsaW5lIG9uIGdpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL20tamFobi9DUklTUFJpLWxpYi1waXBlKS4KCmBgYHtyfQojIGxvYWQgZmlyc3Qgc2VxIHJ1bgpsb2FkKCIuLi9kYXRhL2lucHV0L0RFU2VxMl9yZXN1bHQuUmRhdGEiKQpkZl9tYWluIDwtIERFU2VxX3Jlc3VsdF90YWJsZQoKIyBsb2FkIHNlY29uZCBzZXEgcnVuCmxvYWQoIi4uL2RhdGEvaW5wdXQvREVTZXEyX3Jlc3VsdF8yLlJkYXRhIikKZGZfbWFpbiA8LSBiaW5kX3Jvd3MoZGZfbWFpbiwgREVTZXFfcmVzdWx0X3RhYmxlKQoKIyByZW1vdmUgc2luZ2xlIHJlc3VsdHMgdGFibGUKcm0oREVTZXFfcmVzdWx0X3RhYmxlKQpgYGAKCgojIyBEYXRhIGFubm90YXRpb24KCkRpZmZlcmVudCBhbm5vdGF0aW9uIGNvbHVtbnMgYXJlIGFkZGVkIHRvIHRoZSBtYWluIGRhdGEgZnJhbWUsIGluY2x1ZGluZyBhIHNob3J0IHNnUk5BIGlkZW50aWZpZXIgKGV4Y2x1ZGluZyB0aGUgcG9zaXRpb24gb24gdGhlIGdlbmUpLCBhbiBzZ1JOQSBpbmRleCAoMSB0byA1KSwgYW5kIGdlbm9tZSBhbm5vdGF0aW9uIGZyb20gVW5pcHJvdC4gVGhlIFVuaXByb3QgZGF0YSBpcyBkeW5hbWljYWxseSBkb3dubG9hZGVkIGZvciBldmVyeSB1cGRhdGUgb2YgdGhpcyBwaXBlbGluZSB1c2luZyB0aGVpciB2ZXJ5IHNpbXBsZSBBUEkgKGByZWFkX3RzdigiaHR0cHM6Ly93d3cudW5pcHJvdC5vcmcvdW5pcHJvdC8/cXVlcnk9dGF4b25vbXk6MTExMTcwOCZmb3JtYXQ9dGFiIilgKS4gVGhlIGZ1bGwgbGlzdCBvZiBjb2x1bW5zIHRoYXQgY2FuIGJlIHF1ZXJpZWQgaXMgYXZhaWxhYmxlIFtoZXJlXShodHRwczovL3d3dy51bmlwcm90Lm9yZy9oZWxwL3VuaXByb3RrYl9jb2x1bW5fbmFtZXMpLgpQYXRod2F5IGFubm90YXRpb24gZnJvbSBLRUdHIGlzIGxhdGVyIGluIHRoZSBwaXBlbGluZSBhZGRlZCB1c2luZyB0aGUgYEtFR0dSRVNUYCBwYWNrYWdlLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KZGZfbWFpbiA8LSBkZl9tYWluICU+JQogICMgY29ycmVjdCBhbiBlcnJvciBpbiBzZ1JOQSBuYW1pbmcKICBtdXRhdGUoc2dSTkEgPSBnc3ViKCfigJ0nLCAnMicsIHNnUk5BKSkgJT4lCiAgIyBzcGxpdCBzZ1JOQSBuYW1lcyBpbnRvIHRhcmdldCBnZW5lIGFuZCBwb3NpdGlvbgogIHNlcGFyYXRlKHNnUk5BLCBpbnRvID0gYygic2dSTkFfdGFyZ2V0IiwgInNnUk5BX3Bvc2l0aW9uIiksIHNlcCA9ICJcXHwiLAogICAgcmVtb3ZlID0gRkFMU0UpICU+JQogIAogICMgYWRkIHNnUk5BIGluZGV4IG51bWJlciAoMSB0byBtYXhpbWFsbHkgNSkgYW5kIHR5cGUKICBncm91cF9ieShzZ1JOQV90YXJnZXQpICU+JQogIG11dGF0ZSgKICAgIHNnUk5BX3Bvc2l0aW9uID0gYXMubnVtZXJpYyhzZ1JOQV9wb3NpdGlvbiksCiAgICBzZ1JOQV9pbmRleCA9IHNnUk5BX3Bvc2l0aW9uICU+JSBhcy5mYWN0b3IgJT4lIGFzLm51bWVyaWMsCiAgICBzZ1JOQV90eXBlID0gaWZfZWxzZShncmVwbCgiXm5jXyIsIHNnUk5BKSwgIm5jUk5BIiwgImdlbmUiKSkgJT4lCiAgdW5ncm91cCAlPiUKICAKICAjIG1hcCB0cml2aWFsIG5hbWVzIHRvIExvY3VzVGFncyB1c2luZyBhIG1hbnVhbGx5IGN1cmF0ZWQgbGlzdAogIGxlZnRfam9pbigKICAgIHJlYWRfdHN2KCIuLi9kYXRhL2lucHV0L21hcHBpbmdfdHJpdmlhbF9uYW1lcy50c3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpLAogICAgYnkgPSBjKCJzZ1JOQV90YXJnZXQiID0gImdlbmUiKSkgJT4lCiAgCiAgIyByZW1vdmUgc29tZSBlbXB0eSByb3dzIChOQSB0YXJnZXRzKQogIGZpbHRlcighaXMubmEoc2dSTkFfdGFyZ2V0KSkgJT4lCiAgCiAgIyByZW1vdmUgMiBjb25kaXRpb25zIHdpdGhvdXQgcmVzcG9uc2UKICBmaWx0ZXIoIWNvbmRpdGlvbiAlaW4lIGMoIkJHMTEiLCAiTEMsIDIwMHVFIikpICU+JQogIAogICMgc3BsaXQgY29uZGl0aW9uIGludG8gc2VwYXJhdGUgY29scwogIHNlcGFyYXRlKGNvbmRpdGlvbiwgaW50byA9IGMoImNhcmJvbiIsICJsaWdodCIsICJ0cmVhdG1lbnRfMSIsICJ0cmVhdG1lbnRfMiIpLAogICAgc2VwID0gIiwgIiwgcmVtb3ZlID0gRkFMU0UsIGZpbGwgPSAicmlnaHQiKSAlPiUKICB1bml0ZSgidHJlYXRtZW50IiwgdHJlYXRtZW50XzEsIHRyZWF0bWVudF8yLCBzZXAgPSAiLCAiLCBuYS5ybSA9IFRSVUUpCmBgYAoKT3ZlcnZpZXcgYWJvdXQgdGhlIGRpZmZlcmVudCBjb25kaXRpb25zLgoKYGBge3J9CmRmX2N1bHRpdmF0aW9uX3N1bW1hcnkgPC0gZGZfbWFpbiAlPiUgZ3JvdXBfYnkoY29uZGl0aW9uKSAlPiUKICBzdW1tYXJpemUoCiAgICB0aW1lX3BvaW50cyA9IHBhc3RlKHVuaXF1ZSh0aW1lKSwgY29sbGFwc2UgPSAiLCAiKSwKICAgIGNhcmJvbiA9IHVuaXF1ZShjYXJib24pLAogICAgbGlnaHQgPSB1bmlxdWUobGlnaHQpLAogICAgdHJlYXRtZW50ID0gdW5pcXVlKHRyZWF0bWVudCksCiAgICBtaW5fZml0ID0gbWluKGZpdG5lc3MpLAogICAgbWVkX2ZpdCA9IG1lZGlhbihmaXRuZXNzKSwKICAgIG1heF9maXQgPSBtYXgoZml0bmVzcykpCgpwcmludChkZl9jdWx0aXZhdGlvbl9zdW1tYXJ5KQp3cml0ZV9jc3YoZGZfY3VsdGl2YXRpb25fc3VtbWFyeSwgZmlsZSA9ICIuLi9kYXRhL291dHB1dC9jdWx0aXZhdGlvbl9zdW1tYXJ5LmNzdiIpCmBgYAoKUmV0cmlldmUgZ2VuZSBpbmZvIGZyb20gdW5pcHJvdCBhbmQgbWVyZ2Ugd2l0aCBtYWluIGRhdGEgZnJhbWUuIFdlIG5lZWQgdG8gbWFrZSBhIGN1c3RvbSBmdW5jdGlvbiB0byByZXRyaWV2ZSBhbmQgcGFyc2UgdGhlIGRhdGEgZnJvbSB1bmlwcm90LCBiZWNhdXNlIG9mIGEgYnVnIGluIHRoZSBzZWN1cml0eSBsZXZlbCBvbiBVYnVudHUgMjAuMDQuIFRoZSBmYWxsYmFjayBvcHRpb24gaXMgdG8gbG9hZCBhIGxvY2FsIGNvcHkgb2YgdW5pcHJvdCBhbm5vdGF0aW9uIGZvciB0aGlzIG9yZ2FuaXNtLgoKYGBge3J9CmxpYnJhcnkoaHR0cikKdW5pcHJvdF91cmwgPC0gcGFzdGUwKAogICAiaHR0cHM6Ly93d3cudW5pcHJvdC5vcmcvdW5pcHJvdC8/cXVlcnk9dGF4b25vbXk6MTExMTcwOCZmb3JtYXQ9dGFiJiIsCiAgICJjb2x1bW5zPWlkLGdlbmVzLGdlbmVzKFBSRUZFUlJFRCkscHJvdGVpbl9uYW1lcyxsZW5ndGgsbWFzcyxlYyxkYXRhYmFzZShLRUdHKSIpCgpnZXRfdW5pcHJvdCA8LSBmdW5jdGlvbih1cmwpIHsKICAjIHJlc2V0IHNlY3VyaXR5IGxldmVsLCBjYXVzZWQgYnkgYSBmYXVsdHkgU1NMIGNlcnRpZmljYXRlIG9uIHNlcnZlciBzaWRlLAogICMgc2VlIHRoaXMgdGhyZWFkOiBodHRwczovL2dpdGh1Yi5jb20vRW5zZW1ibC9lbnNlbWJsLXJlc3QvaXNzdWVzLzQyNwogIGh0dHJfY29uZmlnIDwtIGNvbmZpZyhzc2xfY2lwaGVyX2xpc3QgPSAiREVGQVVMVEBTRUNMRVZFTD0xIikKICByZXMgPC0gd2l0aF9jb25maWcoY29uZmlnID0gaHR0cl9jb25maWcsIEdFVCh1cmwpKQogIHNlcnZlcl9lcnJvciA9IHNpbXBsZUVycm9yKCIiKQogIGRmX3VuaXByb3QgPC0gdHJ5Q2F0Y2goCiAgICByZWFkX3Rzdihjb250ZW50KHJlcyksIGNvbF90eXBlcyA9IGNvbHMoKSksCiAgICBlcnJvciA9IGZ1bmN0aW9uKHNlcnZlcl9lcnJvcikgewogICAgICBtZXNzYWdlKCJVbmlwcm90IHNlcnZlciBub3QgYXZhaWxhYmxlLCBmYWxsaW5nIGJhY2sgb24gbG9jYWwgVW5pcHJvdCBEQiBjb3B5IikKICAgICAgcmVhZF90c3YoIi4uL2RhdGEvaW5wdXQvdW5pcHJvdF9zeW5lY2hvY3lzdGlzLnRzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkKICAgIH0KICApCn0KCmRmX3VuaXByb3QgPC0gZ2V0X3VuaXByb3QodW5pcHJvdF91cmwpICU+JQogIHJlbmFtZV93aXRoKHRvbG93ZXIpICU+JQogIHJlbmFtZShsb2N1cyA9IGBjcm9zcy1yZWZlcmVuY2UgKGtlZ2cpYCwgZ2VuZV9uYW1lID0gYGdlbmUgbmFtZXNgLAogICAgZ2VuZV9uYW1lX3Nob3J0ID0gYGdlbmUgbmFtZXMgIChwcmltYXJ5IClgLCBlY19udW1iZXIgPSBgZWMgbnVtYmVyYCwKICAgIHByb3RlaW4gPSBgcHJvdGVpbiBuYW1lc2AsIHVuaXByb3RfSUQgPSBlbnRyeQogICkgJT4lCiAgc2VwYXJhdGVfcm93cyhsb2N1cywgc2VwID0gIjtzeW46IikgJT4lCiAgbXV0YXRlKGxvY3VzID0gc3RyX3JlbW92ZV9hbGwobG9jdXMsICJzeW46fDsiKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShsb2N1cykpCgpkZl9tYWluIDwtIGxlZnRfam9pbihkZl9tYWluLCBmaWx0ZXIoZGZfdW5pcHJvdCwgIWR1cGxpY2F0ZWQobG9jdXMpKSwKICBieSA9ICJsb2N1cyIpCmBgYAoKCiMjIE51bWJlciBvZiBzZ1JOQXMKCkVhY2ggZ2VuZSBpcyByZXByZXNlbnRlZCBieSB1cCB0byBmaXZlIHNnUk5Bcy4gV2UgY2FuIHRlc3QgaWYgYWxsIG9yIG9ubHkgc29tZSBvZiB0aGUgNSBzZ1JOQXMgYXJlICJiZWhhdmluZyIgaW4gdGhlIHNhbWUgd2F5IGluIHRoZSBzYW1lIGNvbmRpdGlvbnMsIG1vcmUgbWF0aGVtYXRpY2FsbHkgc3BlYWtpbmcgd2UgY2FuIGVzdGltYXRlIHRoZSBjb3JyZWxhdGlvbiBvZiBldmVyeSBzZ1JOQSB3aXRoIGFub3RoZXIuIEZpcnN0IGxldCdzIHN1bW1hcml6ZSBob3cgbWFueSBnZW5lcyBoYXZlIDUsIDQsIDMgc2dSTkFzIGFuZCBzbyBvbiBhc3NvY2lhdGVkIHdpdGggdGhlbS4KCmBgYHtyLCAsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSAzLjV9CiMgTiB1bmlxdWUgc2dSTkFzIGluIGRhdGFzZXQKcGFzdGUwKCJOdW1iZXIgb2YgdW5pcXVlIHNnUk5BczogIiwgdW5pcXVlKGRmX21haW4kc2dSTkEpICU+JSBsZW5ndGgpCgojIE4gZ2VuZXMgd2l0aCAxLDIsMyw0IG9yIDUgc2dSTkFzCnBsb3Rfc2dSTkFzX3Blcl9nZW5lIDwtIGRmX21haW4gJT4lCiAgZ3JvdXBfYnkoc2dSTkFfdHlwZSwgc2dSTkFfdGFyZ2V0KSAlPiUKICBzdW1tYXJpemUobl9zZ1JOQXMgPSBsZW5ndGgodW5pcXVlKHNnUk5BX3Bvc2l0aW9uKSksIC5ncm91cHMgPSAiZHJvcF9sYXN0IikgJT4lCiAgY291bnQobl9zZ1JOQXMpICU+JSBmaWx0ZXIobl9zZ1JOQXMgPD0gNSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKG5fc2dSTkFzLCA1OjEpLCB5ID0gbiwgbGFiZWwgPSBuKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX3RleHQoc2l6ZSA9IDMsIG51ZGdlX3kgPSAyMDAsIGNvbG9yID0gZ3JleSgwLjUpKSArCiAgZmFjZXRfZ3JpZCh+IHNnUk5BX3R5cGUpICsKICBsYWJzKHggPSAibiBzZ1JOQXMgLyB0YXJnZXQiLCB5ID0gIm4gdGFyZ2V0cyIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTUwLCAzNTAwKSkgKwogIGN1c3RvbV90aGVtZSgpCgpwcmludChwbG90X3NnUk5Bc19wZXJfZ2VuZSkKc2F2ZV9wbG90KHBsb3Rfc2dSTkFzX3Blcl9nZW5lLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMuNSkKYGBgCgojIE5vcm1hbGl6YXRpb24KCiMjIEZpdG5lc3MgZGlzdHJpYnV0aW9uIG9mIGFsbCBjb25kaXRpb25zCgpCZWZvcmUgYmlvbG9naWNhbCBhbmFseXNpcyBjb250aW51ZXMsIHdlIG5lZWQgdG8gY2hlY2sgaWYgZml0bmVzcyAoYW5kIGxvZzIgRkMgZnJvbSB3aGljaCBpdCBpcyBjYWxjdWxhdGVkKSBpcyBlcXVhbGx5IGRpc3RyaWJ1dGVkLiBGb3IgZXhhbXBsZSwgc3RyaWN0bHkgZXNzZW50aWFsIGdlbmVzIGxpa2Ugcmlib3NvbWFsIGdlbmVzIHNob3VsZCBzaG93IHRoZSBzYW1lIGRlZ3JlZWUgb2YgZGVwbGV0aW9uIG92ZXIgdGltZSwgcmVnYXJkbGVzcyBvZiBjb25kaXRpb24uCgpXZSBjYW4gY29tcGFyZSBmaXRuZXNzIG92ZXIgYWxsIGNvbmRpdGlvbnMgdXNpbmcgYSBzY2F0dGVyIHBsb3QgbWF0cml4LiBXZSBjYW4gc2VlIHRoYXQgc29tZSBjb25kaXRpb25zIGFyZSB2ZXJ5IHNpbWlsYXIgdG8gZWFjaCBvdGhlciwgZm9yIGV4YW1wbGUgdGhlIGNvbmRpdGlvbnMgdHJlYXRlZCB3aXRoIGdsdWNvc2UgKGBMQywgTEwgK2dgLCBgTEMsIExMLCArRCwgK0dgLCBgSEMsIExMICtnYCkuIE90aGVycyBhcmUgbW9yZSBkaXNzaW1pbGFyIHRvIHRoZSByZXN0LCBmb3IgZXhhbXBsZSBgTEMsIElMYCBhbmQgYExDLCBMTCwgK0ZMYC4gVGhleSBhcmUgbW9yZSBhbGlrZSBlYWNoIG90aGVyLCBhbHRob3VnaCBgTEMsIExMLCArRkxgIHNob3VsZCBiZSBtb3JlIGNvbXBhcmFibGUgdG8gYExDLCBMTGAsIGhpbnRpbmcgYXQgZXhwZXJpbWVudGFsIGJpYXMuIEluIHRoaXMgY2FzZSBib3RoIG9mIHRoZXNlIGNvbmRpdGlvbnMgKGFuZCBgTEMsIExMLCArR2ApIHdlcmUgcHJlLWN1bHRpdmF0ZWQgaW4gbG93IGxpZ2h0IGluc3RlYWQgb2YgaGlnaCBsaWdodCwgYXMgb3Bwb3NlZCB0byB0aGUgcmVzdCBvZiB0aGUgc2FtcGxlcy4KCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOH0KZGZfbWFpbiAlPiUgZmlsdGVyKHRpbWUgPT0gMCwgc2dSTkFfaW5kZXggPT0gMSkgJT4lCiAgc2VsZWN0KGxvY3VzLCBjb25kaXRpb24sIGZpdG5lc3MpICU+JQogIGZpbHRlcighaXMubmEobG9jdXMpKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29uZGl0aW9uLCB2YWx1ZXNfZnJvbSA9IGZpdG5lc3MpICU+JQogIHNlbGVjdCgtbG9jdXMpICU+JQogIGN1c3RvbV9zcGxvbShwY2ggPSAxOSwgY2V4ID0gMC4zLCBjb2wgPSBncmV5KDAuNCwgMC40KSwgcHNjYWxlcyA9IDApCmBgYAoKIyMgTm9ybWFsaXphdGlvbiBzdHJhdGVneQoKSW4gb3JkZXIgdG8gYWNjb3VudCBmb3IgZXhwZXJpbWVudGFsIG9yIHF1YW50aWZpY2F0aW9uIGJpYXMsIHdlIGNhbiB0cnkgdG8gbm9ybWFsaXplIHRoZSBsb2cyIEZDIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIGFsbCBzYW1wbGVzLCBhbmQgdGhlbiByZS1jYWxjdWxhdGUgZml0bmVzcy4gVGhlIHVuZGVybHlpbmcgYXNzdW1wdGlvbiBpcyB0aGF0IGUuZy4gZXNzZW50aWFsIGdlbmVzIHNob3VsZCBkZXBsZXRlIGF0IHRoZSBzYW1lIHJhdGUgYW5kIGhlbmNlIHNob3cgaWRlbnRpY2FsIGxvZzIgRkMgYXQgaWRlbnRpY2FsIHRpbWUgcG9pbnRzLiBEaWZmZXJlbnQgdHlwZXMgb2YgZXhwZXJpbWVudGFsIGJpYXMgaW5mbHVlbmNlIGdsb2JhbCBmaXRuZXNzIGRpc3RyaWJ1dGlvbiBhbmQgc2hvdWxkIGJlIHJlZHVjZWQgd2l0aCBub3JtYWxpemF0aW9uLiBIZXJlIHdlIHRyeSBhICdjeWNsaWMgbG9lc3MnIG9yIHF1YW50aWxlIG5vcm1hbGl6YXRpb24gdGhhdCBnYXZlIGdvb2QgcmVzdWx0cyBpbiBhIHF1aWNrIGNvbXBhcmlzb24uCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDV9CiMgY29uc3RydWN0IGEgbm9ybWFsaXphdGlvbiBmdW5jdGlvbiB0aGF0IHRha2VzIHRocmVlIGNvbHVtcyBhcyBpbnB1dCwKIyB0aGUgbnVtZXJpYyB2YXJpYWJsZSB0byBiZSBub3JtYWxpemVkLCB0aGUgY29uZGl0aW9uaW5nIHZhcmlhYmxlCiMgKGNoYXJhY3RlciBvciBmYWN0b3IpLCBhbmQgYW4gSUQgdGhhdCBpZGVudGlmaWVzIGVhY2ggb2JzZXJ2YXRpb24gKHNnUk5BKQphcHBseV9ub3JtID0gZnVuY3Rpb24oaWQsIGNvbmQsIHZhcikgewogIGRmX29yaWcgPC0gdGliYmxlKGlkID0gaWQsIGNvbmQgPSBjb25kLCB2YXIgPSB2YXIpCiAgZGZfbmV3IDwtIHBpdm90X3dpZGVyKGRmX29yaWcsIG5hbWVzX2Zyb20gPSBjb25kLCB2YWx1ZXNfZnJvbSA9IHZhcikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJpZCIpICU+JSBhcy5tYXRyaXggJT4lCiAgbGltbWE6Om5vcm1hbGl6ZUJldHdlZW5BcnJheXMobWV0aG9kID0gInF1YW50aWxlIikgJT4lCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImlkIikgJT4lCiAgcGl2b3RfbG9uZ2VyKC1pZCwgbmFtZXNfdG8gPSAiY29uZCIsIHZhbHVlc190byA9ICJ2YXJfbm9ybSIpCiAgbGVmdF9qb2luKGRmX29yaWcsIGRmX25ldywgYnkgPSBjKCJpZCIsICJjb25kIikpICU+JSBwdWxsKHZhcl9ub3JtKQp9CgojIGFwcGx5IG5vcm1hbGl6YXRpb24KZGZfbWFpbiA8LSBkZl9tYWluICU+JQogIG11dGF0ZShGb2xkQ2hhbmdlID0gMl5sb2cyRm9sZENoYW5nZSkgJT4lCiAgZ3JvdXBfYnkodGltZSkgJT4lCiAgbXV0YXRlKAogICAgRm9sZENoYW5nZV9ub3JtID0gYXBwbHlfbm9ybShzZ1JOQSwgY29uZGl0aW9uLCBGb2xkQ2hhbmdlKSwKICAgIGxvZzJGb2xkQ2hhbmdlID0gbG9nMihGb2xkQ2hhbmdlX25vcm0pCiAgKSAlPiUgdW5ncm91cAoKIyBjb21wYXJlIGVmZmVjdCBvZiBub3JtYWxpemF0aW9uCmRmX21haW4gJT4lIGdyb3VwX2J5KGNvbmRpdGlvbikgJT4lIHNsaWNlKDE6MTAwMDApICU+JQogIGdncGxvdChhZXMoeCA9IGxvZzIoRm9sZENoYW5nZSksIHkgPSBsb2cyKEZvbGRDaGFuZ2Vfbm9ybSksIGNvbG9yID0gZmFjdG9yKHRpbWUpKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIGZhY2V0X3dyYXAofiBjb25kaXRpb24sIG5jb2wgPSA0KSArCiAgY3VzdG9tX3RoZW1lKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzKQpgYGAKCkFub3RoZXIgd2F5IHRvIGxvb2sgYXQgdGhlIHJlc3VsdCBvZiB0aGUgbm9ybWFsaXphdGlvbiBpcyB0byBjb21wYXJlIHRoZSBnbG9iYWwgZGlzdHJpYnV0aW9uIG9mIGxvZzIgRkMgdmFsdWVzLCBhcyBhIGRlbnNpdHkgcGxvdC4KCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KGdncmlkZ2VzKQpkZl9tYWluICU+JSBmaWx0ZXIodGltZSA9PSAxMCkgJT4lCiAgc2VsZWN0KHNnUk5BLCBjb25kaXRpb24sIEZvbGRDaGFuZ2UsIEZvbGRDaGFuZ2Vfbm9ybSkgJT4lCiAgcGl2b3RfbG9uZ2VyKG1hdGNoZXMoIl5Gb2xkIiksIG5hbWVzX3RvID0gIm1ldHJpYyIsIHZhbHVlc190byA9ICJGQyIpICU+JQogIGRpc3RpbmN0ICU+JQogIGdncGxvdChhZXMoeCA9IGxvZzIoRkMpLCB5ID0gY29uZGl0aW9uLCBncm91cCA9IGNvbmRpdGlvbikpICsgCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhmaWxsID0gIiMwMEFGQkI5OSIsIGNvbCA9IGdyZXkoMC40KSkgKwogIGZhY2V0X3dyYXAofiBtZXRyaWMsIG5jb2wgPSA0KSArCiAgbGltcyh4ID0gYygtMiwgMS41KSkgKwogIGN1c3RvbV90aGVtZSgpCmBgYAoKTm93IHdlIG5lZWQgdG8gcmUtY2FsY3VsYXRlIGZpdG5lc3MgYmFzZWQgb24gdGhlIG5vcm1hbGl6ZWQgbG9nMiBGQy4KCmBgYHtyfQpkZl9tYWluIDwtIGRmX21haW4gJT4lCiAgc2VsZWN0KC1Gb2xkQ2hhbmdlLCAtRm9sZENoYW5nZV9ub3JtKSAlPiUKICBncm91cF9ieShzZ1JOQSwgY29uZGl0aW9uKSAlPiUKICBtdXRhdGUoZml0bmVzcyA9IERlc2NUb29sczo6QVVDKHRpbWUsIGxvZzJGb2xkQ2hhbmdlKS8obWF4KHRpbWUpLzIpKSAlPiUKICBhcnJhbmdlKHNnUk5BX3RhcmdldCwgc2dSTkFfaW5kZXgsIGNvbmRpdGlvbiwgdGltZSkKYGBgCgojIEZpdG5lc3Mgc2NvcmUgYWdncmVnYXRpb24KCiMjIENvcnJlbGF0aW9uIG9mIHNnUk5BcwoKRGlmZmVyZW50IG1ldGhvZHMgY2FuIGJlIHVzZWQgdG8gZXN0aW1hdGUgc2ltaWxhcml0eSBiZXR3ZWVuIHNhbXBsZXMgKHNnUk5BcykuIEZvciBleGFtcGxlLCBmYWN0b3IgYW5hbHlzaXMgaXMgYSBtZXRob2QgdG8gZGlzc2VjdCB1bmRlcmx5aW5nIHNvdXJjZXMgb2YgdmFyaWF0aW9uIHdpdGhpbiB0aGUgZGF0YXNldCwgYW5kIHRoZSBjb250cmlidXRpb24gdG8gb3ZlcmFsbCB2YXJpYXRpb24uIFRoZSBtb3N0IGZhbW91cyBleGFtcGxlIGlzIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkuIFdlIGNhbiBhbHNvIHVzZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2Ygc2dSTkFzIHRvIGVhY2ggb3RoZXIgdG8gc2VlIGlmIG9uZSBvZiB0aGUgc2dSTkFzIGNvbnRyaWJ1dGVzIHN0cm9uZ2VyIHRvIG92ZXJhbGwgdmFyaWF0aW9uLgoKVGhpcyBpcyBhbiBleGFtcGxlIG9mIGFuIGFwcGFyZW50bHkgc3RyaWN0bHkgZXNzZW50aWFsIGdlbmUsIGVuY29kaW5nIHRoZSByaWJvc29tYWwgcHJvdGVpbiBgcnBzMTBgLiBNb3N0IG9mIHRoZSBzZ1JOQSByZXByZXNzb3Igc3RyYWlucyBhcmUgZGVwbGV0ZWQsIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHNnUk5BcyBpcyBoaWdoLiBUaGUgc3RyZW5ndGggb2YgZGVwbGV0aW9uIHZhcmllcyB0aG91Z2gsIGFuZCB0aGUgc3RyYWluIHdpdGggc2dSTkEgMyBpcyBub3QgZGVwbGV0ZWQgYXQgYWxsLiBXZSB3YW50IHRvIGdpdmUgaGlnaGVyIHdlaWdodHMgdG8gc2dSTkFzIHRoYXQgY29ycmVsYXRlIHdlbGwgd2l0aCBlYWNoIG90aGVyLCBhbmQvb3Igc2hvdyBzdHJvbmdlciBlZmZlY3QgKGRlcGxldGlvbi9lbnJpY2htZW50KS4KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNS41fQpwbG90X3NnUk5BX3JpYm9fZXhhbXBsZSA8LSBkZl9tYWluICU+JSBmaWx0ZXIoc2dSTkFfdGFyZ2V0ID09ICJycHMxMCIpICU+JQogIG11dGF0ZShzZ1JOQV9pbmRleCA9IGZhY3RvcihzZ1JOQV9pbmRleCwgMTo1KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdGltZSwgeSA9IGxvZzJGb2xkQ2hhbmdlLCBjb2xvciA9IHNnUk5BX2luZGV4KSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKyBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgZmFjZXRfd3JhcCh+IGNvbmRpdGlvbiwgbmNvbCA9IDQpICsKICBjdXN0b21fdGhlbWUoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9yYW5nZSg1KSkKCnByaW50KHBsb3Rfc2dSTkFfcmlib19leGFtcGxlKQpzYXZlX3Bsb3QocGxvdF9zZ1JOQV9yaWJvX2V4YW1wbGUsIHdpZHRoID0gNywgaGVpZ2h0ID0gNS41KQpgYGAKCkEgY29ycmVsYXRpb24gc2NvcmUgY2FuIGJlIGNhbGN1bGF0ZWQgYnkgY29tcHV0aW5nIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBvZiBhbGwgc2dSTkFzIHRvIGVhY2ggb3RoZXIuIFRoaXMgc2NvcmUgaXMgcm9idXN0bHkgc3VtbWFyaXplZCBieSB0YWtpbmcgdGhlIG1lZGlhbiwgYW5kIHJlc2NhbGluZyBpdCBmcm9tIHRoZSByZXNwZWN0aXZlIG1pbmltYSBhbmQgbWF4aW1hIFstMSwgMV0gdG8gWzAsIDFdLiBUaGlzIHNjb3JlIHNlcnZlcyBhcyBhIHdlaWdodCBjb21wb25lbnQgZm9yIGVhY2ggc2dSTkEgdG8gY2FsY3VsYXRlIHRoZSAoZ2xvYmFsKSB3ZWlnaHRlZCBtZWFuIG9mIGxvZzIgRkMgb3ZlciBhbGwgc2dSTkFzLiBUaGUgc2NvcmUgaGFzIHRoZSBjaGFyYWN0ZXJpc3RpYyB0aGF0IGl0IGdpdmVzIGEgd2VpZ2h0IG9mIDEgZm9yIGFuIHNnUk5BIHBlcmZlY3RseSBjb3JyZWxhdGVkIHdpdGggYWxsIG90aGVyIHNnUk5BcyBvZiB0aGUgc2FtZSBnZW5lLCBhbmQgYSB3ZWlnaHQgb2YgMCBmb3Igc2dSTkFzIHBlcmZlY3RseSBhbnRpLWNvcnJlbGF0ZWQgdG8gdGhlIG90aGVyIHNnUk5Bcy4KCkZvciBhIG1hdHJpeCBvZiAkeCA9IDEgLi4gbSQgc2dSTkFzIGFuZCAkeSA9IDEgLi4gbiQgb2JzZXJ2YXRpb25zIChtZWFzdXJlbWVudHMpLCB0aGUgY29ycmVsYXRpb24gJFIkIG9mIG9uZSBzZ1JOQSB0byBhbm90aGVyIGlzIGNhbGN1bGF0ZWQgdXNpbmcgUGVhcnNvbidzIG1ldGhvZDoKCiRSX3g9Y29yKFtsb2dfMkZDX3t4MSx5MX0gLi4uIGxvZ18yRkNfe3gxLHlufV0sIFtsb2dfMkZDX3t4Mix5MX0gLi4uIGxvZ18yRkNfe3gyLHlufV0pJAoKVGhlIGNvcnJlbGF0aW9uIHdlaWdodCBvZiBvbmUgc2dSTkEgaXMgdGhlbiBjYWxjdWxhdGVkIGFzIG1lZGlhbiBvZiBhbGwgJFIkIHJlc2NhbGVkIGJldHdlZW4gMCBhbmQgMS4KCiR3X3ggPSBcZnJhY3sxICsgbWVkaWFuKFJfMSwgUl8yLCAuLi4sIFJfbSl9ezJ9JAoKVGhlIGZvbGxvd2luZyBleGFtcGxlIHNob3dzIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSA1IGBycHMxMGAgc2dSTkFzLCBhbmQgdGhlaXIgd2VpZ2h0cy4gVGhlIHNlbGYgY29ycmVsYXRpb24gb2YgZWFjaCBzZ1JOQSAoUiA9IDEpIGlzIHJlbW92ZWQgcHJpb3IgdG8gd2VpZ2h0IGRldGVybWluYXRpb24uCgpgYGB7ciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CmNvcl9tYXRyaXggPC0gZGZfbWFpbiAlPiUgZmlsdGVyKHNnUk5BX3RhcmdldCA9PSAicnBzMTAiKSAlPiUgdW5ncm91cCAlPiUKICBzZWxlY3Qoc2dSTkFfaW5kZXgsIGxvZzJGb2xkQ2hhbmdlLCBjb25kaXRpb24sIHRpbWUpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjKCJjb25kaXRpb24iLCAidGltZSIpLCB2YWx1ZXNfZnJvbSA9IGxvZzJGb2xkQ2hhbmdlKSAlPiUKICBhcnJhbmdlKHNnUk5BX2luZGV4KSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJzZ1JOQV9pbmRleCIpICU+JQogIGFzLm1hdHJpeCAlPiUgdCAlPiUgY29yKG1ldGhvZCA9ICJwZWFyc29uIikKCndlaWdodHMgPC0gY29yX21hdHJpeCAlPiUgcmVwbGFjZSguLCAuID09IDEsIE5BKSAlPiUKICBhcHBseSgyLCBmdW5jdGlvbih4KSBtZWRpYW4oeCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgcmVzY2FsZShmcm9tID0gYygtMSwgMSksIHRvID0gYygwLCAxKSkKCiMgcGxvdCBoZWF0bWFwCmxhdHRpY2U6OmxldmVscGxvdChjb3JfbWF0cml4ICU+JSByZXBsYWNlKC4sIC4gPT0gMSwgTkEpLAogIGNvbC5yZWdpb25zID0gY3VzdG9tX3JhbmdlKDIwKSkKCiMgcHJpbnQgd2VpZ2h0cwp3ZWlnaHRzCmBgYAoKLS0tLS0tLS0tLQoKTm93IHdlIGNhbiBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IHdpbGwgY29tcHV0ZSB3ZWlnaHRzIGZvciBhbGwgc2dSTkFzLCBhbmQgYWRkIHRoZSB3ZWlnaHRzIHRvIHRoZSBkYXRhIHNldC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9CmRldGVybWluZV9jb3JyIDwtIGZ1bmN0aW9uKGluZGV4LCB2YWx1ZSwgY29uZGl0aW9uLCB0aW1lKSB7CiAgIyBtYWtlIGNvcnJlbGF0aW9uIG1hdHJpeAogIGRmIDwtIGRhdGEuZnJhbWUoaW5kZXggPSBpbmRleCwgdmFsdWUgPSB2YWx1ZSwgY29uZGl0aW9uID0gY29uZGl0aW9uLCB0aW1lID0gdGltZSkKICBjb3JfbWF0cml4IDwtIHBpdm90X3dpZGVyKGRmLCBuYW1lc19mcm9tID0gYygiY29uZGl0aW9uIiwgInRpbWUiKSwgdmFsdWVzX2Zyb20gPSB2YWx1ZSkgJT4lCiAgICBhcnJhbmdlKGluZGV4KSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJpbmRleCIpICU+JQogICAgYXMubWF0cml4ICU+JSB0ICU+JSBjb3IobWV0aG9kID0gInBlYXJzb24iKQogIAogICMgZGV0ZXJtaW5lIHdlaWdodHMKICB3ZWlnaHRzIDwtIGNvcl9tYXRyaXggJT4lIHJlcGxhY2UoLiwgLiA9PSAxLCBOQSkgJT4lCiAgICBhcHBseSgyLCBmdW5jdGlvbih4KSBtZWRpYW4oeCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgICBzY2FsZXM6OnJlc2NhbGUoZnJvbSA9IGMoLTEsIDEpLCB0byA9IGMoMCwgMSkpICU+JQogICAgZW5mcmFtZSgiaW5kZXgiLCAid2VpZ2h0IikgJT4lIG11dGF0ZShpbmRleCA9IGFzLm51bWVyaWMoaW5kZXgpKSAlPiUKICAgIG11dGF0ZSh3ZWlnaHQgPSByZXBsYWNlKHdlaWdodCwgaXMubmEod2VpZ2h0KSwgMSkpCiAgCiAgIyByZXR1cm4gdmVjdG9yIG9mIHdlaWdodHMgdGhlIHNhbWUgb3JkZXIgYW5kIGxlbmd0aCAKICAjIGFzIHNnUk5BIGluZGV4IHZlY3RvcgogIGxlZnRfam9pbihkZiwgd2VpZ2h0cywgYnkgPSAiaW5kZXgiKSAlPiUgcHVsbCh3ZWlnaHQpCn0KCmRmX21haW4gPC0gZGZfbWFpbiAlPiUKICBncm91cF9ieShzZ1JOQV90YXJnZXQpICU+JQogIG11dGF0ZShzZ1JOQV9jb3JyZWxhdGlvbiA9IGRldGVybWluZV9jb3JyKHNnUk5BX2luZGV4LAogICAgbG9nMkZvbGRDaGFuZ2UsIGNvbmRpdGlvbiwgdGltZSkpCmBgYAoKCiMjIEVmZmljaWVuY3kgb2Ygc2dSTkFzCgpUaGUgY29ycmVsYXRpb24gb2YgZWFjaCBzZ1JOQSB3aXRoIGVhY2ggb3RoZXIgaXMgYSAiZ2xvYmFsIiBwYXJhbWV0ZXIgYXMgaXQgaXMgaWRlbnRpY2FsIG92ZXIgYWxsIGNvbmRpdGlvbnMuIEEgc2Vjb25kIGdsb2JhbCBwYXJhbWV0ZXIsICoqc2dSTkEgZWZmaWNpZW5jeSoqLCBjYW4gYmUgb2J0YWluZWQgdXNpbmcgYSBzaW1pbGFyIGFwcHJvYWNoLiBXZSBleHBlY3QgdGhhdCBmaXRuZXNzIG9mIGFsbCBzZ1JOQXMgZm9yIG9uZSBnZW5lIGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCBiZWNhdXNlIHNnUk5BcyBhcmUgbm90IGlkZWFsIHJlcGxpY2F0ZSBtZWFzdXJlbWVudHMuIFRoZXkgYXJlIGJpYXNlZCBieSBwb3NpdGlvbiBlZmZlY3RzIGFuZCBvZmYtdGFyZ2V0IGJpbmRpbmcsIHNlZSBbV2FuZyBldCBhbC4sIE5hdHVyZSBDb21tcywgMjAxOF0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE0NjctMDE4LTA0ODk5LXgpIGZvciBhIHZlcnkgaW5zaWdodGZ1bCBhbmQgY29tcHJlaGVuc2l2ZSBhbmFseXNpcyBvZiB0aGUgbnVtYmVyIGFuZCBwb3NpdGlvbiBvZiBzZ1JOQXMgcmVxdWlyZWQgdG8gZXN0aW1hdGUgZ2VuZSBmaXRuZXNzLiAKCldlIGNhbGN1bGF0ZSBzZ1JOQSBlZmZpY2llbmN5ICRFJCBhcyB0aGUgbWVkaWFuIGFic29sdXRlIGZpdG5lc3MgKEFVQyBvZiBsb2cyRkMgb3ZlciB0aW1lKSBvZiBhbiBzZ1JOQSAkeCA9IDEgLi4gbSQgb3ZlciBhbGwgb2JzZXJ2YXRpb25zIFtjb25kaXRpb25zXSAkeSA9IDEgLi4gbiQuCgokRV94PW1lZGlhbihhYnMoZml0bmVzc197eDEsIHkxfSwgZml0bmVzc197eDEsIHkyfSwgLi4uLCBmaXRuZXNzX3t4MSwgeW59KSkkCgpUbyBub3JtYWxpemUgYmV0d2VlbiBhbGwgc2dSTkFzLCAkRSQgaXMgcmVzY2FsZWQgdG8gYSByYW5nZSBiZXR3ZWVuIDAgYW5kIDEuCgokRV94PVxmcmFje0VfeH17bWF4KEVfMSwgRV8yLCAuLi4sIEVfbSl9JAoKYGBge3J9CmRmX21haW4gPC0gZGZfbWFpbiAlPiUgZ3JvdXBfYnkoc2dSTkFfdGFyZ2V0KSAlPiUKICBtdXRhdGUoc2dSTkFfZWZmaWNpZW5jeSA9IGF2ZShmaXRuZXNzLCBzZ1JOQV9pbmRleCwgRlVOID0gZnVuY3Rpb24oeCkgbWVkaWFuKGFicyh4KSkpICU+JQogICAgey4vbWF4KC4pfSkKYGBgCgpUaGlzIGlzIHRoZSByZXN1bHRpbmcgc2dSTkEgZWZmaWNpZW5jeSBmb3IgdGhlIGV4YW1wbGUgZ2VuZSBhYm92ZSwgYHJwczEwYC4KCmBgYHtyfQpkZl9tYWluICU+JSBmaWx0ZXIoc2dSTkFfdGFyZ2V0ID09ICJycHMxMCIpICU+JSB1bmdyb3VwICU+JQogIHNlbGVjdChzZ1JOQV9pbmRleCwgc2dSTkFfZWZmaWNpZW5jeSkgJT4lIGRpc3RpbmN0ICU+JSAKICBhcnJhbmdlKHNnUk5BX2luZGV4KSAlPiUgZGVmcmFtZQpgYGAKCgojIyBQb3NpdGlvbiBiaWFzIG9mIHNnUk5BIHJlcHJlc3Npb24KClBsb3QgdGhlICoqd2VpZ2h0IG9mIGVhY2ggc2dSTkEqKiB0byBzZWUgaWYgdGhlcmUgaXMgYSBkZXBlbmRlbmN5IGJldHdlZW4gY29ycmVsYXRpb24gYW5kIHNnUk5BIHBvc2l0aW9uLiBUaGVyZSBpcyBubyBzaWduaWZpY2FudCB0cmVuZC4KCldlIGNhbiBhbHNvIHF1YW50aWZ5IGhvdyBtYW55IGdlbmVzIGhhdmUgc3Ryb25nbHkgY29ycmVsYXRlZCBzZ1JOQXMgYW5kIGhvdyBtYW55IGhhdmUgb3V0bGllcnMuIEluIG9yZGVyIHRvIGRvIHRoaXMsIHRoZSBtZWRpYW4gd2VpZ2h0IG9mIHRoZSAodXAgdG8pIDUgc2dSTkFzIHBlciBnZW5lIGlzIHBsb3R0ZWQuIEdlbmVyYWxseSwgdGhlIG1lZGlhbiB3ZWlnaHQgcmFuZ2VzIGJldHdlZW4gMC41IGFuZCAxLjAsIHNob3dpbmcgb24gYXZlcmFnZSBnb29kIGNvcnJlbGF0aW9uLgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSAzfQpwbG90X3NnUk5BX2NvcnJlbGF0aW9uIDwtIGRmX21haW4gJT4lCiAgc2VsZWN0KHNnUk5BX3RhcmdldCwgc2dSTkFfaW5kZXgsIHNnUk5BX2NvcnJlbGF0aW9uKSAlPiUKICBmaWx0ZXIoc2dSTkFfaW5kZXggPD0gNSkgJT4lCiAgZGlzdGluY3QgJT4lCiAgIyBwbG90CiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKHNnUk5BX2luZGV4KSwgeSA9IHNnUk5BX2NvcnJlbGF0aW9uKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gIiIpICsKICBsYWJzKHggPSAic2dSTkEgcG9zaXRpb24iLCB5ID0gImNvcnJlbGF0aW9uIikgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIGMoeSA9IG1lZGlhbih4KSswLjA3LCAKICAgIGxhYmVsID0gcm91bmQobWVkaWFuKHgpLCAyKSksIGdlb20gPSAidGV4dCIsIHNpemUgPSAzKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgYyh5ID0gMS4xLCAKICAgIGxhYmVsID0gbGVuZ3RoKHgpKSwgZ2VvbSA9ICJ0ZXh0IiwgY29sb3IgPSBncmV5KDAuNSksIHNpemUgPSAzKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0wLjE1LCAxLjE1KSkgKwogIGN1c3RvbV90aGVtZSgpCgpwbG90X3NnUk5BX2NvcnJlbGF0aW9uX2hpc3QgPC0gZGZfbWFpbiAlPiUKICBzZWxlY3Qoc2dSTkFfdGFyZ2V0LCBzZ1JOQV9pbmRleCwgc2dSTkFfY29ycmVsYXRpb24pICU+JQogIGZpbHRlcihzZ1JOQV9pbmRleCA8PSA1KSAlPiUKICBkaXN0aW5jdCAlPiUgZ3JvdXBfYnkoc2dSTkFfdGFyZ2V0KSAlPiUKICBzdW1tYXJpemUoCiAgICBtZWRpYW5fc2dSTkFfY29ycmVsYXRpb24gPSBtZWRpYW4oc2dSTkFfY29ycmVsYXRpb24pLAogICAgbWluX3NnUk5BX2NvcnJlbGF0aW9uID0gbWluKHNnUk5BX2NvcnJlbGF0aW9uKQogICkgJT4lCiAgIyBwbG90CiAgZ2dwbG90KGFlcyh4ID0gbWVkaWFuX3NnUk5BX2NvcnJlbGF0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA0MCwgZmlsbCA9IGN1c3RvbV9jb2xvcnNbMV0sIGFscGhhID0gMC43KSArCiAgY3VzdG9tX3RoZW1lKCkKCnNhdmVfcGxvdChwbG90X3NnUk5BX2NvcnJlbGF0aW9uX2hpc3QsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKc2F2ZV9wbG90KHBsb3Rfc2dSTkFfY29ycmVsYXRpb24sIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKZ2dhcnJhbmdlKHBsb3Rfc2dSTkFfY29ycmVsYXRpb24sIHBsb3Rfc2dSTkFfY29ycmVsYXRpb25faGlzdCwgbmNvbCA9IDIpCmBgYAoKU2Vjb25kLCB0aGUgYmluZGluZyBwb3NpdGlvbiBvZiB0aGUgc2dSTkFzIGNvdWxkIGJlIGNvcnJlbGF0ZWQgdG8gdGhlIHN0cmVuZ3RoIG9mIHJlcHJlc3Npb24uIEluIG90aGVyIHdvcmRzIHNnUk5BcyBiaW5kaW5nIGNsb3NlciB0byB0aGUgcHJvbW90ZXIgY291bGQgaGF2ZSBzdHJvbmdlciBhYmlsaXR5IHRvIHJlcHJlc3MgYSBnZW5lLCBzZWUgRmlndXJlIDEgQiBpbiBbV2FuZyBldCBhbC4sIE5hdHVyZSBDb21tcywgMjAxOF0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE0NjctMDE4LTA0ODk5LXgpLiBXZSBwbG90ICoqc2dSTkEgZWZmaWNpZW5jeSoqIGZvciBnZW5lcyBvbmx5LCBiZWNhdXNlIHRoZSBhYnNvbHV0ZSBtYWpvcml0eSBvZiB0aG9zZSBoYXMgNSBzZ1JOQXMuCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDN9CnBsb3Rfc2dSTkFfZWZmaWNpZW5jeSA8LSBkZl9tYWluICU+JQogIGZpbHRlcihzZ1JOQV9pbmRleCA8PSA1LCBzZ1JOQV90eXBlID09ICJnZW5lIikgJT4lCiAgc2VsZWN0KHNnUk5BX3RhcmdldCwgc2dSTkFfaW5kZXgsIHNnUk5BX2VmZmljaWVuY3kpICU+JSBkaXN0aW5jdCAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3Ioc2dSTkFfaW5kZXgpLCB5ID0gc2dSTkFfZWZmaWNpZW5jeSkpICsKICBnZW9tX2JveHBsb3Qobm90Y2ggPSBGQUxTRSwgb3V0bGllci5zaGFwZSA9ICIuIikgKwogIGxhYnMoeCA9ICJzZ1JOQSBwb3NpdGlvbiAocmVsYXRpdmUpIiwgeSA9ICJyZXByZXNzaW9uIGVmZmljaWVuY3kiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0wLjE1LCAxLjE1KSkgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIGMoeSA9IG1lZGlhbih4KSswLjA3LCAKICAgIGxhYmVsID0gcm91bmQobWVkaWFuKHgpLCAyKSksIGdlb20gPSAidGV4dCIsIHNpemUgPSAzKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgYyh5ID0gMS4xLCAKICAgIGxhYmVsID0gbGVuZ3RoKHgpKSwgZ2VvbSA9ICJ0ZXh0IiwgY29sb3IgPSBncmV5KDAuNSksIHNpemUgPSAzKSArCiAgY3VzdG9tX3RoZW1lKCkKCgpwbG90X3NnUk5BX2VmZmljaWVuY3lfaGlzdCA8LSBkZl9tYWluICU+JQogIGZpbHRlcihzZ1JOQV9pbmRleCA8PSA1LCBzZ1JOQV90eXBlID09ICJnZW5lIikgJT4lCiAgc2VsZWN0KHNnUk5BX3RhcmdldCwgc2dSTkFfcG9zaXRpb24sIHNnUk5BX2VmZmljaWVuY3kpICU+JSBkaXN0aW5jdCAlPiUKICBncm91cF9ieShzZ1JOQV9wb3NpdGlvbikgJT4lCiAgc3VtbWFyaXplKHNnUk5BX2VmZmljaWVuY3kgPSBtZWRpYW4oc2dSTkFfZWZmaWNpZW5jeSksIG5fcG9zID0gbigpKSAlPiUKICBmaWx0ZXIobl9wb3MgPj0gMTApICU+JQogIGdncGxvdChhZXMoeCA9IHNnUk5BX3Bvc2l0aW9uLCB5ID0gc2dSTkFfZWZmaWNpZW5jeSkpICsKICBsYWJzKHggPSAic2dSTkEgcG9zaXRpb24gKG50KSIsIHkgPSAicmVwcmVzc2lvbiBlZmZpY2llbmN5IikgKwogIGdlb21fcG9pbnQoY29sID0gYWxwaGEoY3VzdG9tX2NvbG9yc1s1XSwgMC41KSkgKwogIGdlb21fc21vb3RoKCkgKwogIGN1c3RvbV90aGVtZSgpCgpzYXZlX3Bsb3QocGxvdF9zZ1JOQV9lZmZpY2llbmN5LCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnNhdmVfcGxvdChwbG90X3NnUk5BX2VmZmljaWVuY3lfaGlzdCwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpnZ2FycmFuZ2UocGxvdF9zZ1JOQV9lZmZpY2llbmN5LCBwbG90X3NnUk5BX2VmZmljaWVuY3lfaGlzdCwgbmNvbCA9IDIpCmBgYAoKRXhwb3J0IGRyYWZ0ICoqRmlndXJlIDEqKiBmb3IgbWFudXNjcmlwdC4KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNS41fQpwbG90X3NlbGVjdGVkX3NnUk5BcyA8LSBkZl9tYWluICU+JQogIGZpbHRlcigKICAgIGdyZXBsKCJjdHJsWzEtNV0kfHJwczEwJCIsIHNnUk5BX3RhcmdldCksIAogICAgY29uZGl0aW9uICVpbiUgYygiSEMsIEhMIiwgIkhDLCBMTCIsICJMQywgSUwiLCAiTEMsIExMIikpICU+JQogIG11dGF0ZSgKICAgIHNnUk5BX2luZGV4MiA9IGFzLm51bWVyaWMoc3RyX2V4dHJhY3Qoc2dSTkFfdGFyZ2V0LCAiWzEtOV0kIikpLAogICAgc2dSTkFfaW5kZXggPSBjYXNlX3doZW4oc2dSTkFfcG9zaXRpb24gPT0gMCB+IHNnUk5BX2luZGV4MiwgVFJVRSB+IHNnUk5BX2luZGV4KSwKICAgIHNnUk5BX3RhcmdldCA9IHN0cl9leHRyYWN0KHNnUk5BX3RhcmdldCwgIlthLXpBLVpdKiIpCiAgKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gbG9nMkZvbGRDaGFuZ2UsIGNvbG9yID0gZmFjdG9yKHNnUk5BX2luZGV4KSkpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGZhY2V0X2dyaWQoc2dSTkFfdGFyZ2V0IH4gY29uZGl0aW9uKSArCiAgY3VzdG9tX3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IDApICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTQuNSwgMi41KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcmFuZ2UoNSkpCgpzdmcoZmlsZW5hbWUgPSAiLi4vZmlndXJlcy9maWd1cmUxLnN2ZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gNS41KQpnZ2FycmFuZ2UobmNvbCA9IDIsIG5yb3cgPSAyLCB3aWR0aHMgPSBjKDAuNiwgMC40KSwgbGFiZWxzID0gTEVUVEVSU1sxOjRdLCBmb250LmxhYmVsID0gbGlzdF9mb250cGFycywKICBwbG90X3NnUk5Bc19wZXJfZ2VuZSArIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEyLDEyLDEyLDEyKSwgInBvaW50cyIpKSwKICBwbG90X3NnUk5BX2VmZmljaWVuY3kgKyB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygyNiwxMiwxMiwxMiksICJwb2ludHMiKSksCiAgcGxvdF9zZWxlY3RlZF9zZ1JOQXMgKyB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygxMiwtNCwxMiwxNCksICJwb2ludHMiKSksCiAgcGxvdF9zZ1JOQV9jb3JyZWxhdGlvbiArIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDI2LDEyLDEyLDEyKSwgInBvaW50cyIpKQopCmRldi5vZmYoKQpgYGAKCkV4cG9ydCAqKnN1cHBsZW1lbnRhbCBmaWd1cmUgd2l0aCBhbGwgcmlib3NvbWFsIGdlbmVzKiogKHJwcypOTiovcnBsKk5OKikuCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDEwfQpwbG90X3NnUk5Bc19yaWJvc29tZSA8LSBkZl9tYWluICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHNnUk5BX3RhcmdldCwgInJwW3NsXVswLTldKiQiKSkgJT4lCiAgZmlsdGVyKGNvbmRpdGlvbiA9PSAiTEMsIExMIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdGltZSwgeSA9IGxvZzJGb2xkQ2hhbmdlLCBjb2xvciA9IGZhY3RvcihzZ1JOQV9pbmRleCkpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICBmYWNldF93cmFwKH4gc2dSTkFfdGFyZ2V0LCBuY29sID0gNykgKwogIGN1c3RvbV90aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcmFuZ2UoNSkpCgpwcmludChwbG90X3NnUk5Bc19yaWJvc29tZSkKYGBgCgoKIyBHZW5lIGZpdG5lc3MgY2FsY3VsYXRpb24KCiMjIFN1bW1hcml6ZSBzZ1JOQSBmaXRuZXNzIHRvIGdlbmUgZml0bmVzcwoKV2l0aCB0aGUgY29ycmVsYXRpb24gYW5kIHRoZSBlZmZpY2llbmN5IHBlciBzZ1JOQSwgd2UgY2FuIGNvbXB1dGUgdGhlICoqd2VpZ2h0ZWQgbWVhbiBvZiBhbGwgc2dSTkFzKiouIEZvciBjb21wYXJpc29uLCB3ZSBhbHNvIHRlc3Qgc2ltcGxlIHN0cmF0ZWdpZXMgc3VjaCBhcyB0aGUgc3RhbmRhcmQgKiphcml0aG1ldGljIG1lYW4qKiBhbmQgYSB0b3AgMSBhbmQgdG9wIDIgc2dSTkFzIHN0cmF0ZWd5LiBNZXRyaWNzIGFyZSBjYWxjdWxhdGVkIGZvciBsb2cyIEZDLCBhbmQgZml0bmVzcy4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGZfY29udHJvbHMgPC0gZGZfbWFpbiAlPiUgdW5ncm91cCAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc2dSTkFfdGFyZ2V0LCAiY3RybFswLTldKyQiKSkKCmRmX2dlbmUgPC0gZGZfbWFpbiAlPiUKICAKICAjIGtlZXAgYWxsIGFubm90YXRpb24gY29sdW1ucwogIGdyb3VwX2J5KHNnUk5BX3RhcmdldCwgc2dSTkFfdHlwZSwgbG9jdXMsIGdlbmVfbmFtZSwgY29uZGl0aW9uLCAKICAgIGNhcmJvbiwgbGlnaHQsIHRyZWF0bWVudCwgdGltZSkgJT4lCiAgCiAgIyBzdW1tYXJpemUgRkMgYW5kIGZpdG5lc3MuLi4KICBzdW1tYXJpemUoLmdyb3VwcyA9ICJkcm9wIiwKICAgIAogICAgIyBsb2cyIEZDCiAgICBtZWFuX2xvZzJGb2xkQ2hhbmdlID0gbWVhbihsb2cyRm9sZENoYW5nZSksCiAgICB3bWVhbl9sb2cyRm9sZENoYW5nZSA9IHdlaWdodGVkLm1lYW4obG9nMkZvbGRDaGFuZ2UsIHNnUk5BX2NvcnJlbGF0aW9uICogc2dSTkFfZWZmaWNpZW5jeSksCiAgICB0b3AxX2xvZzJGb2xkQ2hhbmdlID0gbG9nMkZvbGRDaGFuZ2Vbd2hpY2gubWF4KHNnUk5BX2VmZmljaWVuY3kpXSwKICAgIHRvcDJfbG9nMkZvbGRDaGFuZ2UgPSBtZWFuKGxvZzJGb2xkQ2hhbmdlW29yZGVyKHNnUk5BX2VmZmljaWVuY3ksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjJdXSksCiAgICBzZF9sb2cyRm9sZENoYW5nZSA9IHNkKGxvZzJGb2xkQ2hhbmdlKSwKICAgIAogICAgIyBmaXRuZXNzCiAgICBtZWFuX2ZpdG5lc3MgPSBtZWFuKGZpdG5lc3MpLAogICAgd21lYW5fZml0bmVzcyA9IHdlaWdodGVkLm1lYW4oZml0bmVzcywgc2dSTkFfY29ycmVsYXRpb24gKiBzZ1JOQV9lZmZpY2llbmN5KSwKICAgIHRvcDFfZml0bmVzcyA9IGZpdG5lc3Nbd2hpY2gubWF4KHNnUk5BX2VmZmljaWVuY3kpXSwKICAgIHRvcDJfZml0bmVzcyA9IG1lYW4oZml0bmVzc1tvcmRlcihzZ1JOQV9lZmZpY2llbmN5LCBkZWNyZWFzaW5nID0gVFJVRSlbMToyXV0pLAogICAgc2RfZml0bmVzcyA9IHNkKGZpdG5lc3MpLAogICAgCiAgICAjIGFwcGx5IHNpZ25pZmljYW5jZSB0ZXN0LCBNYW5uLVdoaXRuZXkgVSB0ZXN0CiAgICBwX3ZhbHVlID0gd2lsY294LnRlc3QoZml0bmVzcywgZmlsdGVyKGRmX2NvbnRyb2xzLCBjb25kaXRpb24gPT0gdW5pcXVlKGNvbmRpdGlvbikpJGZpdG5lc3MpJHAudmFsdWUKICApCmBgYAoKU2luY2Ugc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIGlzIHRlc3RlZCBmb3IgbWFueSBnZW5lcyBpbiBwYXJhbGxlbCwgdGhlIHAtdmFsdWUgb2J0YWluZWQgZnJvbSBNV1UgdGVzdCBzaG91bGQgYmUgbXVsdGlwbGUtaHlwb3RoZXNpcyBjb3JyZWN0ZWQuIEZvciB0aGlzIHB1cnBvc2Ugd2UgdXNlIHRoZSBCZW5qYW1pbmktSG9jaGJlcmcgbWV0aG9kLiBXZSBhbHNvIGNhbGN1bGF0ZSBhIHNjb3JlIHRha2luZyBib3RoIGVmZmVjdCBzaXplIGFuZCBwLXZhbHVlIGludG8gYWNjb3VudCwgYWNjb3JkaW5nIHRvIHRoZSBwdWJsaWNhdGlvbiBmcm9tIFtXYW5nIGV0IGFsLiwgTmF0IENvbW0sIDIwMThdKGh0dHA6Ly9keC5kb2kub3JnLzEwLjEwMzgvczQxNDY3LTAxOC0wNDg5OS14KS4gVGhpcyBzY29yZSBpcyBzaW1wbHkgdGhlIGFic29sdXRlIGZpdG5lc3Mgc2NvcmUgbXVsdGlwbGllZCBieSB0aGUgbmVnYXRpdmUgbG9nMTAgcC12YWx1ZS4KCgpgYGB7cn0KZGZfZ2VuZSA8LSBkZl9nZW5lICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgbXV0YXRlKAogICAgcF92YWx1ZV9hZGogPSBwLmFkanVzdChwX3ZhbHVlLCBtZXRob2QgPSAiQkgiKSwKICAgIHNjb3JlID0gYWJzKHdtZWFuX2ZpdG5lc3MpKi1sb2cxMChwX3ZhbHVlX2FkaikKICApICU+JSB1bmdyb3VwCmBgYAoKCkEgY29tcGFyaXNvbiBvZiBsb2cyIEZDIGFnZ3JlZ2F0ZWQgYnkgdGhlIGRpZmZlcmVudCBtZXRob2Qgc2hvd3MgY2xlYXIgZGlmZmVyZW5jZXMuIEZvciB0aGUgZXhhbXBsZSBnZW5lIGBycHMxMGAgdGhlIHdlaWdodGVkIG1lYW4gYW5kIHRoZSB0b3AgbWV0aG9kIGdpdmUgc2ltaWxhciByZXN1bHRzLCByZXByZXNlbnRhdGl2ZSBvZiB0aGUgc3Ryb25nZXIgaW5mbHVlbmNlIGZyb20gaGlnaGx5IGRlcGxldGVkIHNnUk5BIHJlcHJlc3Npb24gc3RyYWlucy4gVGhlIHJlZ3VsYXIgbWVhbiBpcyByb2J1c3QsIGJ1dCAic2hhbGxvdyIsIHByb2JhYmx5IHVuZGVyZXN0aW1hdGluZyB0aGUgcmVhbCBlZmZlY3QgbiBmaXRuZXNzLiBUaGUgdG9wIDEgbWV0aG9kIHNpbXBseSBwaWNrcyB0aGUgbW9zdCBkZXBsZXRlZC9lbnJpY2hlZCBzZ1JOQSAob3ZlciBhbGwgY29uZGl0aW9ucykgYXMgcmVwcmVzZW50YXRpdmUuCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDUuNX0KZGZfZ2VuZSAlPiUgZmlsdGVyKHNnUk5BX3RhcmdldCA9PSAicnBzMTAiKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IG1hdGNoZXMoIltuMTJdX2xvZzJGb2xkQ2hhbmdlIiksIAogICAgbmFtZXNfdG8gPSAibWV0cmljIiwgdmFsdWVzX3RvID0gImxvZzJGb2xkQ2hhbmdlIikgJT4lCiAgbXV0YXRlKG1ldHJpYyA9IHN0cl9yZW1vdmUobWV0cmljLCAiX2xvZzJGb2xkQ2hhbmdlIikpICU+JQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBsb2cyRm9sZENoYW5nZSwgCiAgICB5bWluID0gbG9nMkZvbGRDaGFuZ2Utc2RfbG9nMkZvbGRDaGFuZ2UsIAogICAgeW1heCA9IGxvZzJGb2xkQ2hhbmdlK3NkX2xvZzJGb2xkQ2hhbmdlLCBjb2xvciA9IGZjdF9pbm9yZGVyKG1ldHJpYykpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgZ2VvbV9saW5lcmFuZ2Uoc2l6ZSA9IDEpICsKICBmYWNldF93cmFwKH4gY29uZGl0aW9uLCBuY29sID0gNCkgKwogIGN1c3RvbV90aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0zLjc1LCAwLjc1KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcmFuZ2UoNCkpCmBgYAoKClRoaXMgcGxvdCBzaG93cyBhIGNvbXBhcmlzb24gb2YgdGhlIDQgbWV0aG9kcyBmb3IgdGhlIGZpcnN0IDM2IGdlbmVzIGJ5IGFscGhhYmV0aWNhbCBvcmRlciwgZm9yIG9uZSBzZWxlY3RlZCBjb25kaXRpb24gb25seSAoMSUgQ08yLCBCRzExLCAxLDAwMCDCtW1vbCBwaG90b25zIG0tMSBzLTEpLiBIZXJlIHdlIGNhbiBzZWUgdGhhdCB0aGUgdG9wMSBtZXRob2QgaXMgb2Z0ZW4gYnV0IG5vdCBhbHdheXMgcmVwcmVzZW50YXRpdmUgZm9yIHRoZSBnZW5lOiBGb3IgYXBjRCBvciBhcGNGLCBpdCBkb2VzIG5vdCBzZWVtIHJlcHJlc2VudGF0aXZlIGNvbXBhcmVkIHRvIHRoZSBtZWFuLCB3ZWlnaHRlZCBtZWFuLCBhbmQgdG9wMiBtZXRob2RzLgoKYGBge3IsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA5fQpkZl9nZW5lICU+JSBmaWx0ZXIoCiAgICBnZW5lX25hbWUgJWluJSB1bmlxdWUoLmRhdGFbWyJnZW5lX25hbWUiXV0pWzE6MzZdLAogICAgY29uZGl0aW9uID09ICJIQywgSEwiCiAgKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IG1hdGNoZXMoIltuMTJdX2xvZzJGb2xkQ2hhbmdlIiksIG5hbWVzX3RvID0gIm1ldHJpYyIsIHZhbHVlc190byA9ICJsb2cyRm9sZENoYW5nZSIpICU+JQogIG11dGF0ZShtZXRyaWMgPSBzdHJfcmVtb3ZlKG1ldHJpYywgIl9sb2cyRm9sZENoYW5nZSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gbG9nMkZvbGRDaGFuZ2UsIAogICAgeW1pbiA9IGxvZzJGb2xkQ2hhbmdlLXNkX2xvZzJGb2xkQ2hhbmdlLAogICAgeW1heCA9IGxvZzJGb2xkQ2hhbmdlK3NkX2xvZzJGb2xkQ2hhbmdlLCBjb2xvciA9IGZjdF9pbm9yZGVyKG1ldHJpYykpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgZ2VvbV9saW5lcmFuZ2Uoc2l6ZSA9IDEpICsKICBmYWNldF93cmFwKH4gc2dSTkFfdGFyZ2V0LCBuY29sID0gNykgKwogIGN1c3RvbV90aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygtNSwgNSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tX3JhbmdlKDQpKQpgYGAKCiMjIEdsb2JhbCBkaXN0cmlidXRpb24gb2YgZ2VuZSBmaXRuZXNzCgpHbG9iYWwgZGlzdHJpYnV0aW9uIG9mIHdlaWdodGVkIG1lYW4gZml0bmVzcyBmb3IgYWxsIGdlbmVzLiBFZmZlY3Qgb2YgbmNSTkEgcmVwcmVzc2lvbiBzZWVtcyB0byBiZSBtdWNoIGxvd2VyIHRoYW4gZWZmZWN0IG9mIGdlbmUgcmVwcmVzc2lvbi4KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF9hbGxfZml0bmVzc19oaXN0IDwtIGRmX2dlbmUgJT4lIGZpbHRlcih0aW1lID09IDApICU+JQogIGdncGxvdChhZXMoeCA9IHdtZWFuX2ZpdG5lc3MsIGZpbGwgPSBzZ1JOQV90eXBlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygwLCAxMDAwKSkgKwogIGZhY2V0X3dyYXAoIH4gY29uZGl0aW9uLCBuY29sID0gNCkgKwogIGN1c3RvbV90aGVtZSgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzW2MoMzo0KV0pCgpwcmludChwbG90X2FsbF9maXRuZXNzX2hpc3QpCnNhdmVfcGxvdChwbG90X2FsbF9maXRuZXNzX2hpc3QsIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKYGBgCgojIyBHZW5lIGZpdG5lc3MgdnMgc2lnbmlmaWNhbmNlCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDMuNX0KcGxvdF9hbGxfZml0bmVzc192b2xjIDwtIGRmX2dlbmUgJT4lIGZpbHRlcih0aW1lID09IDAsCiAgICBjb25kaXRpb24gJWluJSBjKCJIQywgSEwiLCAiTEMsIExMIikpICU+JQogIGFycmFuZ2Uoc2dSTkFfdHlwZSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gd21lYW5fZml0bmVzcywgeSA9IC1sb2cxMChwX3ZhbHVlX2FkaiksIGNvbCA9IHNnUk5BX3R5cGUpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNSkgKwogIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYyhzZXEoLTgsIC0wLjUsIDAuMSksIHNlcSgwLjUsIDgsIDAuMSkpLAogICAgeSA9IDQvYyhzZXEoOCwgMC41LCAtMC4xKSwgc2VxKDAuNSwgOCwgMC4xKSkpLAogICAgYWVzKHggPSB4LCB5ID0geSwgc2hhcGUgPSBOVUxMLCBjb2wgPSBOVUxMKSwgbHR5ID0gMikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNywgNyksIHlsaW0gPSBjKDAsIDQpKSArCiAgY3VzdG9tX3RoZW1lKGFzcGVjdCA9IDEsIGxlZ2VuZC5wb3NpdGlvbiA9ICJsZWZ0IiwgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjQsICJjbSIpKSArCiAgZmFjZXRfd3JhcCh+IGNvbmRpdGlvbikgKwogIGxhYnMoeCA9ICJmaXRuZXNzIiwgeSA9IGV4cHJlc3Npb24oIi1sb2ciWzEwXSoiIHAtdmFsdWUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzWzM6NF0pICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMSwgMTkpKQoKcHJpbnQocGxvdF9hbGxfZml0bmVzc192b2xjKQpzYXZlX3Bsb3QocGxvdF9hbGxfZml0bmVzc192b2xjLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMpCmBgYAoKIyMgQmVoYXZpb3Igb2YgY29udHJvbCBzZ1JOQXMKClRlbiBzZ1JOQXMgd2VyZSBpbmNsdWRlZCBpbiB0aGUgbGlicmFyeSB0aGF0IGhhdmUgbm8gZ2VuZS1zcGVjaWZpYyB0YXJnZXRzLiBUaGUgZm9sbG93aW5nIHBsb3Qgc2hvd3MgdGhhdCB0aGVzZSBuZWdhdGl2ZSBjb250cm9scyBkbyBub3QgaGF2ZSBhbiBlZmZlY3Qgb24gc3RyYWluIGZpdG5lc3MsIGV4Y2VwdCBwcm9iYWJseSAyIHNnUk5BcyBpbiBvbmUgc3BlY2lmaWMgY29uZGl0aW9uLgoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1LjV9CnBsb3RfY29udHJvbHNfc2dSTkFzIDwtIGRmX21haW4gJT4lIGZpbHRlcihncmVwbCgiY3RybCIsIHNnUk5BX3RhcmdldCkpICU+JQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBsb2cyRm9sZENoYW5nZSwgY29sb3IgPSBzZ1JOQV90YXJnZXQpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgeWxpbSgtNSwgNSkgKwogIGZhY2V0X3dyYXAofiBjb25kaXRpb24sIG5jb2wgPSA0KSArCiAgY3VzdG9tX3RoZW1lKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcmFuZ2UoMTApKQoKcHJpbnQocGxvdF9jb250cm9sc19zZ1JOQXMpCnNhdmVfcGxvdChwbG90X2NvbnRyb2xzX3NnUk5Bcywgd2lkdGggPSA3LCBoZWlnaHQgPSA1LjUpCmBgYAoKIyBHZW5lIGVucmljaG1lbnQKClRvIHBsb3QgZ2VuZSBmaXRuZXNzIGZvciB0aGUgZW56eW1lcyBvZiBjZW50cmFsIGNhcmJvbiBtZXRhYm9saXNtLCB3ZSBuZWVkIGEgY29tcGxldGUgbGlzdCBvZiBlbnp5bWVzIGFuZCB0aGUgZ2VuZXMgdGhhdCB0aGV5IGFyZSBtYXBwZWQgdG8uIFRvIGxpc3QgdGhlIGRpZmZlcmVudCAqKktFR0cgZGF0YWJhc2VzKiogdGhhdCBjYW4gYmUgcXVlcmllZCwgdXNlIGBsaXN0RGF0YWJhc2VzKClgLiBHZW5lLXBhdGh3YXkgbWFwcGluZ3MgYXJlIG9idGFpbmVkIGFuZCBtZXJnZWQgd2l0aCBwYXRod2F5IG5hbWVzIGFuZCBnZW5lL2VuenltZSBuYW1lcy4KCgpgYGB7cn0KIyBnZXQgbWFwcGluZyBvZiBwYXRod2F5cyBmb3IgZWFjaCBnZW5lCmRmX2tlZ2cgPC0ga2VnZ0xpbmsoInBhdGh3YXkiLCAic3luIikgJT4lCiAgZW5mcmFtZShuYW1lID0gImxvY3VzIiwgdmFsdWUgPSAia2VnZ19wYXRod2F5X2lkIikgJT4lCiAgCiAgIyBnZXQgbGlzdCBvZiBwYXRod2F5cyB3aXRoIG5hbWUvSUQgcGFpcnMKICBsZWZ0X2pvaW4oYnkgPSAia2VnZ19wYXRod2F5X2lkIiwKICAgIGtlZ2dMaXN0KCJwYXRod2F5IiwgInN5biIpICU+JQogICAgZW5mcmFtZShuYW1lID0gImtlZ2dfcGF0aHdheV9pZCIsIHZhbHVlID0gImtlZ2dfcGF0aHdheSIpCiAgKSAlPiUKICAKICAjIGdldCBsaXN0IG9mIGdlbmUvZW56eW1lIG5hbWVzCiAgbGVmdF9qb2luKGJ5ID0gImxvY3VzIiwKICAgIGtlZ2dMaXN0KCJzeW4iKSAlPiUKICAgIGVuZnJhbWUobmFtZSA9ICJsb2N1cyIsIHZhbHVlID0gImtlZ2dfZ2VuZSIpICU+JQogICAgbXV0YXRlKGtlZ2dfZ2VuZV9zaG9ydCA9IHN0cl9leHRyYWN0KGtlZ2dfZ2VuZSwgIl5bYS16QS1aMC05XSo7IikgJT4lIAogICAgICBzdHJfcmVtb3ZlKCI7IikpCiAgKSAlPiUKICAKICAjIHRyaW0gdXNlbGVzcyBwcmVmaXhlcwogIG11dGF0ZSgKICAgIGxvY3VzID0gc3RyX3JlbW92ZShsb2N1cywgInN5bjoiKSwKICAgIGtlZ2dfcGF0aHdheV9pZCA9IHN0cl9yZW1vdmUoa2VnZ19wYXRod2F5X2lkLCAicGF0aDoiKSwKICAgIGtlZ2dfcGF0aHdheSA9IHN0cl9yZW1vdmUoa2VnZ19wYXRod2F5LCAiIC0gU3luZWNob2N5c3RpcyBzcC4gUENDIDY4MDMiKQogICkKCmhlYWQoZGZfa2VnZykKYGBgCgojIyBGaXRuZXNzIHBlciBwYXRod2F5CgpTb21ldGltZXMgZXZlbiBzbWFsbCBlZmZlY3RzIGluIGZpdG5lc3MgY2FuIGJlIHJlbGV2YW50IGlmIHNldmVyYWwgZ2VuZXMgb2YgdGhlIHNhbWUgcGF0aHdheSAob3IgaXNvLWVuenltZXMpIGFyZSBhZmZlY3RlZC4gQSBzaW1wbGUgZml0bmVzcyB0aHJlc2hvbGQgd2lsbCBub3QgcmV2ZWFsIHRob3NlIGNoYW5nZXMuIEluIHN1Y2ggY2FzZXMgYSBtb3JlIG51YW5jZWQgYXBwcm9hY2ggY2FuIGJlIHRha2VuLCBhIGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgKEdTRUEpLiBTZXZlcmFsIHBhY2thZ2VzIGV4aXN0IHRvIHRlc3QgaWYgZnVuY3Rpb25hbGx5IHJlbGF0ZWQgZ2VuZXMgYXJlIGVucmljaGVkLCBkZXBsZXRlZCwgb3IgYm90aCBhdCB0aGUgc2FtZSB0aW1lIC8gdGhlIHNhbWUgY29uZGl0aW9ucy4KCkJlZm9yZSB3ZSB0ZXN0IGZvciBlbnJpY2htZW50IG9mIGFzc29jaWF0ZWQgcGF0aHdheXMvR08gdGVybXMsIHdlIGNhbiBoYXZlIGEgbG9vayBhdCB0aGUgZ2VuZXJhbCBkZXBsZXRpb24vZW5yaWNobWVudCBwZXIgS0VHRyBwYXRod2F5LiBUaGUgZml0bmVzcyBkaXN0cmlidXRpb24gcGVyIHBhdGh3YXkgY2FuIGJlIHZpc3VhbGl6ZWQgdXNpbmcgYSB2aW9saW4tIG9yIHNjYXR0ZXIgcGxvdC4KCmBgYHtyLCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSA1LjV9CnBsb3RfbWVkaWFuX2ZpdG5lc3Nfa2VnZyA8LSBkZl9nZW5lICU+JSBmaWx0ZXIodGltZSA9PSAwKSAlPiUKICBpbm5lcl9qb2luKGRmX2tlZ2csIGJ5ID0gImxvY3VzIikgJT4lCiAgZ3JvdXBfYnkoa2VnZ19wYXRod2F5LCBjb25kaXRpb24pICU+JQogIHN1bW1hcml6ZSguZ3JvdXBzID0gImRyb3AiLAogICAgZml0bmVzcyA9IG1lZGlhbih3bWVhbl9maXRuZXNzKSwKICAgIG5fZ2VuZXMgPSBuKCkKICApICU+JSBmaWx0ZXIobl9nZW5lcyA+PSAyMCkgJT4lCiAgbXV0YXRlKGtlZ2dfcGF0aHdheSA9IHBhc3RlMChzdHJfc3ViKGtlZ2dfcGF0aHdheSwgMSwgMjUpLCAiLi4iKSkgJT4lCiAgbXV0YXRlKGtlZ2dfcGF0aHdheSA9IGZjdF9yZW9yZGVyKGtlZ2dfcGF0aHdheSwgZml0bmVzcywgLmRlc2MgPSBUUlVFKSkgJT4lCiAgCiAgZ2dwbG90KGFlcyh4ID0gZml0bmVzcywgeSA9IGtlZ2dfcGF0aHdheSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5VTEwsIGNvbG9yID0gZ3JleSgwLjUpLCBmaWxsID0gZ3JleSgwLjkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjb25kaXRpb24pKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbHR5ID0gMiwgY29sb3IgPSBncmV5KDAuNSkpICsKICBsYWJzKHggPSAibWVkaWFuIGZpdG5lc3MiLCB5ID0gIiIpICsKICBjdXN0b21fdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjI1LCAwLjI1KSwgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjQsICJjbSIpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX3JhbmdlKDExKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcmFuZ2UoMTEpKQoKcHJpbnQocGxvdF9tZWRpYW5fZml0bmVzc19rZWdnKQpgYGAKCkV4cG9ydCBkcmFmdCAqKkZpZ3VyZSAyKiogZm9yIG1hbnVzY3JpcHQuCldlIGFkZCBwaG90b3N5c3RlbSBJIGFuZCBJSSBnZW5lcyBhcyBleGFtcGxlcyBmb3IgZGlmZmVyZW50aWFsIGRlcGxldGlvbi4gQSBoZWF0bWFwLgoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA0fQpwbG90X3NnUk5Bc19wczEgPC0gZGZfZ2VuZSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChzZ1JOQV90YXJnZXQsICJwc2FbQS1aXSoiKSwgdGltZSA9PSAwKSAlPiUKICBtdXRhdGUod21lYW5fZml0bmVzcyA9IHdtZWFuX2ZpdG5lc3MgJT4lIHJlcGxhY2UoLiwgLiA+IDQsIDQpICU+JSByZXBsYWNlKC4sIC4gPCAtNCwgLTQpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBmY3RfcmV2KHNnUk5BX3RhcmdldCksIGZpbGwgPSB3bWVhbl9maXRuZXNzKSkgKwogIGdlb21fdGlsZSgpICsgY3VzdG9tX3RoZW1lKCkgKwogIGxhYnModGl0bGUgPSAiUGhvdG9zeXN0ZW0gSSIsIHggPSAiIiwgeSA9ICIiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gYyhjdXN0b21fY29sb3JzWzFdLCBncmV5KDAuOSksIGN1c3RvbV9jb2xvcnNbMl0pLAogICAgbGltaXRzID0gYygtNCwgNCkpCgpwbG90X3NnUk5Bc19wczIgPC0gZGZfZ2VuZSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChzZ1JOQV90YXJnZXQsICJwc2JbQS1aXSoiKSwgdGltZSA9PSAwKSAlPiUKICBtdXRhdGUod21lYW5fZml0bmVzcyA9IHdtZWFuX2ZpdG5lc3MgJT4lIHJlcGxhY2UoLiwgLiA+IDQsIDQpICU+JSByZXBsYWNlKC4sIC4gPCAtNCwgLTQpKSAlPiUKICBtdXRhdGUoc2dSTkFfdGFyZ2V0ID0gc3RyX3JlcGxhY2Uoc2dSTkFfdGFyZ2V0LCAicHNiMTMiLCAicHNiVyIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBmY3RfcmV2KHNnUk5BX3RhcmdldCksIGZpbGwgPSB3bWVhbl9maXRuZXNzKSkgKwogIGdlb21fdGlsZSgpICsgY3VzdG9tX3RoZW1lKCkgKwogIGxhYnModGl0bGUgPSAiUGhvdG9zeXN0ZW0gSUkiLCB4ID0gIiIsIHkgPSAiIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoY3VzdG9tX2NvbG9yc1sxXSwgZ3JleSgwLjkpLCBjdXN0b21fY29sb3JzWzJdKSwKICAgIGxpbWl0cyA9IGMoLTQsIDQpKQoKZ2dhcnJhbmdlKG5jb2wgPSAyLCBwbG90X3NnUk5Bc19wczEsIHBsb3Rfc2dSTkFzX3BzMikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDd9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL2ZpZ3VyZTIuc3ZnIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA3KQpnZ2FycmFuZ2UobmNvbCA9IDIsIHdpZHRocyA9IGMoMC42NSwgMC4zNSksCiAgZ2dhcnJhbmdlKG5yb3cgPSAyLCBoZWlnaHRzID0gIGMoMC4zNCwgMC42NiksIGxhYmVscyA9IExFVFRFUlNbMToyXSwgZm9udC5sYWJlbCA9IGxpc3RfZm9udHBhcnMsCiAgICBwbG90X2FsbF9maXRuZXNzX3ZvbGMgKyB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygxNCwtOCwxNCw0MCksICJwb2ludHMiKSksCiAgICBwbG90X21lZGlhbl9maXRuZXNzX2tlZ2cgKyB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYyg2LDEyLDEyLDEyKSwgInBvaW50cyIpKSksCiAgZ2dhcnJhbmdlKG5yb3cgPSAyLCBoZWlnaHRzID0gIGMoMC40LCAwLjYpLCBsYWJlbHMgPSBMRVRURVJTWzM6NF0sIGZvbnQubGFiZWwgPSBsaXN0X2ZvbnRwYXJzLAogICAgcGxvdF9zZ1JOQXNfcHMxICsgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMTIsMCwtMTQsMCksICJwb2ludHMiKSksCiAgICBwbG90X3NnUk5Bc19wczIgKyB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygxMiwwLDAsMCksICJwb2ludHMiKSkKICApCikKZGV2Lm9mZigpCmBgYAoKIyMgR2VuZSBlbnJpY2htZW50IGFuYWx5c2lzIChLRUdHKQoKV2UgdXNlIHRoZSBmdW5jdGlvbnMgYGtlZ2dhYCBmb3IgS0VHRyBlbnJpY2htZW50IGFuYWx5c2lzIGFuZCBgZ29hbmFgIGZvciBHTyB0ZXJtIGVucmljaG1lbnQgZnJvbSB0aGUgYGxpbW1hYCBwYWNrYWdlLiBCb3RoIGZ1bmN0aW9ucyB0ZXN0IGZvciBvdmVyIG9yIHVuZGVyLXJlcHJlc2VudGF0aW9uIG9mIGdlbmVzIGFzc29jaWF0ZWQgd2l0aCBjZXJ0YWluIHBhdGh3YXlzIG9yIEdPIHRlcm1zLiBUaGUgZnVuY3Rpb25zIGRvbid0IHRha2UgdGhlIHN0cmVuZ3RoIG9mIGRpZmZlcmVudGlhbCBmaXRuZXNzIGludG8gYWNjb3VudCAoREY7IHRoZSBkZXBsZXRpb24vZW5yaWNobWVudCBvdmVyIHRpbWUpLgoKCmBgYHtyfQpkZl9rZWdnX2VucmljaG1lbnQgPC0gbGFwcGx5KHVuaXF1ZShkZl9nZW5lJGNvbmRpdGlvbiksIGZ1bmN0aW9uKGNvbmQpIHsKICBkZl9nZW5lICU+JSBmaWx0ZXIoCiAgc2dSTkFfdHlwZSA9PSAiZ2VuZSIsIHRpbWUgPT0gMCwKICBjb25kaXRpb24gPT0gY29uZCkgJT4lCiAgCiAgIyBmaWx0ZXIgZm9yIGRpZmZlcmVudGlhbCBmaXRuZXNzIChERikgZ2VuZXMKICBmaWx0ZXIoIWJldHdlZW4od21lYW5fZml0bmVzcywgLTIuMCwgMi4wKSwgIWlzLm5hKGxvY3VzKSkgJT4lCiAgCiAgIyBwZXJmb3JtIEtFR0cgZW5yaWNobWVudAogIHB1bGwobG9jdXMpICU+JSBrZWdnYShzcGVjaWVzLktFR0cgPSAic3luIikgJT4lCiAgbXV0YXRlKGNvbmRpdGlvbiA9IGNvbmQpCn0pICU+JSBiaW5kX3Jvd3MKCmhlYWQoZGZfa2VnZ19lbnJpY2htZW50KQpgYGAKCk5vdyB3ZSB2aXN1YWxpemUgdGhlIHBhdGh3YXlzIHRoYXQgYXJlIG1vc3QgZW5yaWNoZWQgZm9yIERGIGdlbmVzLiBJdCB0dXJucyBvdXQgdGhhdCByaWJvc29tYWwgcHJvdGVpbnMgYXJlIGV4dHJlbWVseSBkZXBsZXRlZCBhbmQgdGhlcmVmb3JlIHNjb3JlIGhpZ2ggb24gdGhlIG5lZ2F0aXZlIGxvZzEwIHAtdmFsdWUgZm9yIHBhdGh3YXkgZW5yaWNobWVudC4KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNS41fQpkZl9rZWdnX2VucmljaG1lbnQgJT4lCiAgcmVuYW1lKGtlZ2dfcGF0aHdheSA9IFBhdGh3YXkpICU+JQogIGdyb3VwX2J5KGtlZ2dfcGF0aHdheSkgJT4lIGZpbHRlcihOID49IDIwKSAlPiUKICBzZWxlY3Qoa2VnZ19wYXRod2F5LCBjb25kaXRpb24sIFAuREUpICU+JQogIG11dGF0ZShsb2cxMF9wX3ZhbHVlID0gLWxvZzEwKFAuREUpLCAua2VlcCA9ICJ1bnVzZWQiKSAlPiUKICBtdXRhdGUoa2VnZ19wYXRod2F5ID0gcGFzdGUwKHN0cl9zdWIoa2VnZ19wYXRod2F5LCAxLCAyNSksICIuLiIpKSAlPiUKICAKICAjIG1ha2UgY29ycmVsYXRpb24gcGxvdAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjb25kaXRpb24sIHZhbHVlc19mcm9tID0gbG9nMTBfcF92YWx1ZSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJrZWdnX3BhdGh3YXkiKSAlPiUgYXMubWF0cml4ICU+JQogIGNvcnJwbG90KGlzLmNvcnIgPSBGQUxTRSwgdGwuY29sID0gZ3JleSgwLjUpLCB0bC5jZXggPSAwLjgsCiAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGN1c3RvbV9jb2xvcnNbYygxLDUsMildKSgxMCksIGNvbC5saW0gPSBjKDAsIDIwKSkKYGBgCgoKIyBVbnN1cGVydmlzZWQgY2x1c3RlcmluZyBvZiBnZW5lcwoKIyMgQ2x1c3RlciBnZW5lcyBieSBzaW1pbGFyaXR5CgpXZSBjYW4gZGV2aXNlIGEgZ2VuZXJhbGl6ZWQgYHRpZHl2ZXJzZWAgZnJpZW5kbHkgZnVuY3Rpb24gdG8gY2x1c3RlciBhIG5hbWUgdmFyaWFibGUgYnkgYSB2YWx1ZSwgZ3JvdXBlZCBieSBvbmUgb3IgbW9yZSBncm91cGluZyB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBjbHVzdGVyIGdlbmVzIChuYW1lKSBieSBmaXRuZXNzICh2YWx1ZSkgb3ZlciBzZXZlcmFsIGNvbmRpdGlvbnMgKGdyb3VwcykuIFRoZSBvdXRwdXQgaXMgYSBmYWN0b3Igd2l0aCByZS1vcmRlcmVkIGxldmVscy4KCmBgYHtyfQpmY3RfY2x1c3RlciA8LSBmdW5jdGlvbih2YXJpYWJsZSwgZ3JvdXAsIHZhbHVlLCBtZXRob2QgPSAid2FyZC5EMiIpIHsKICBkZiA8LSB0aWJibGUodmFyaWFibGUgPSB2YXJpYWJsZSwgZ3JvdXAgPSBncm91cCwgdmFsdWUgPSB2YWx1ZSkKICBkZiA8LSBwaXZvdF93aWRlcihkZiwgbmFtZXNfZnJvbSA9IGdyb3VwLCB2YWx1ZXNfZnJvbSA9IHZhbHVlKQogIG1hdCA8LSBhcy5tYXRyaXgoY29sdW1uX3RvX3Jvd25hbWVzKGRmLCB2YXIgPSAidmFyaWFibGUiKSkKICBjbCA8LSBoY2x1c3QoZGlzdChtYXQpLCBtZXRob2QgPSBtZXRob2QpCiAgb3JkIDwtIG9yZGVyLmRlbmRyb2dyYW0oYXMuZGVuZHJvZ3JhbShjbCkpCiAgZmFjdG9yKHZhcmlhYmxlLCB1bmlxdWUodmFyaWFibGUpW29yZF0pCn0KYGBgCgpIZWF0IG1hcCBvZiBmaXRuZXNzIGZvciAqYWxsIGdlbmVzIGFuZCBhbGwgY29uZGl0aW9ucyouCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDIuMn0KcGxvdF9oZWF0bWFwX2FsbCA8LSBkZl9nZW5lICU+JSBmaWx0ZXIodGltZSA9PSAwLCAhaXMubmEobG9jdXMpKSAlPiUKICBtdXRhdGUobG9jdXMgPSBmY3RfY2x1c3Rlcihsb2N1cywgY29uZGl0aW9uLCB3bWVhbl9maXRuZXNzKSkgJT4lCiAgbXV0YXRlKHdtZWFuX2ZpdG5lc3MgPSB3bWVhbl9maXRuZXNzICU+JSByZXBsYWNlKC4sIC4gPiA0LCA0KSAlPiUgcmVwbGFjZSguLCAuIDwgLTQsIC00KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9jdXMsIHkgPSBjb25kaXRpb24sIGZpbGwgPSB3bWVhbl9maXRuZXNzKSkgKwogIGdlb21fdGlsZSgpICsgY3VzdG9tX3RoZW1lKGxlZ2VuZC5wb3MgPSAicmlnaHQiKSArCiAgbGFicyh4ID0gcGFzdGUwKCJnZW5lcyAoIiwgbGVuZ3RoKHVuaXF1ZShkZl9nZW5lJGxvY3VzKSksIikiKSwgeSA9ICIiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBjKGN1c3RvbV9jb2xvcnNbMV0sIGdyZXkoMC45KSwgY3VzdG9tX2NvbG9yc1syXSksCiAgICBsaW1pdHMgPSBjKC00LCA0KSkKCnByaW50KHBsb3RfaGVhdG1hcF9hbGwpCnNhdmVfcGxvdChwbG90X2hlYXRtYXBfYWxsLCB3aWR0aCA9IDgsIGhlaWdodCA9IDIuMikKYGBgCgoKTm93IHdlIGNhbiBwbG90IF9hbGxfIGdlbmVzLCBhIHN1YnNldCB3aXRoIF9vbmx5IHNpZ25pZmljYW50IGdlbmVzXywgYW5kIGEgZGVuZHJvZ3JhbSBmb3IgY2x1c3RlcmluZy4gVGhlIHJlc3VsdCBpcyBoYXJkIHRvIGludGVycHJldC4gV2l0aCBzb21lIGV4Y2VwdGlvbnMsIG1vc3QgZ2VuZXMgYXJlIGdyb3VwZWQgaW4gYnJvYWQgdW5zcGVjaWZpYyBjbHVzdGVycyB0aGF0IGRvIG5vdCByZXZlYWwgY2xlYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRyZWF0bWVudCB2YXJpYWJsZXMgYW5kIGZpdG5lc3Mgb3V0Y29tZS4KCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDR9CiMgcHJlcGFyZSBuZXcgZGYgYW5kIHBsb3QgaGVhdG1hcApkZl9oZWF0bWFwIDwtIGRmX2dlbmUgJT4lIGZpbHRlcih0aW1lID09IDAsICFpcy5uYShsb2N1cykpICU+JQogIGdyb3VwX2J5KGxvY3VzKSAlPiUgZmlsdGVyKGFueSghYmV0d2Vlbih3bWVhbl9maXRuZXNzLCAtNCwgNCkpKSAlPiUgdW5ncm91cCAlPiUKICBtdXRhdGUobG9jdXMgPSBmY3RfY2x1c3Rlcihsb2N1cywgY29uZGl0aW9uLCB3bWVhbl9maXRuZXNzKSkgJT4lCiAgbXV0YXRlKHdtZWFuX2ZpdG5lc3MgPSB3bWVhbl9maXRuZXNzICU+JSByZXBsYWNlKC4sIC4gPiA4LCA4KSAlPiUgcmVwbGFjZSguLCAuIDwgLTgsIC04KSkKCnBsb3RfaGVhdG1hcF9zaWcgPC0gZGZfaGVhdG1hcCAlPiUKICBnZ3Bsb3QoYWVzKHggPSBsb2N1cywgeSA9IGNvbmRpdGlvbiwgZmlsbCA9IHdtZWFuX2ZpdG5lc3MpKSArCiAgZ2VvbV90aWxlKCkgKyBjdXN0b21fdGhlbWUobGVnZW5kLnBvcyA9ICJyaWdodCIpICsKICBsYWJzKHggPSBwYXN0ZTAoImdlbmVzICgiLCBsZW5ndGgodW5pcXVlKGRmX2dlbmUkbG9jdXMpKSwiKSIpLCB5ID0gIiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoY3VzdG9tX2NvbG9yc1sxXSwgZ3JleSgwLjkpLCBjdXN0b21fY29sb3JzWzJdKSwKICAgIGxpbWl0cyA9IGMoLTgsIDgpKQoKIyBwcmVwYXJlIGRpc3Qgb2JqZWN0IGZvciBjbHVzdGVyaW5nIGFuZCBwbG90IGRlbmQKZGlzdF9oZWF0bWFwIDwtIGRmX2hlYXRtYXAgJT4lIHNlbGVjdChsb2N1cywgY29uZGl0aW9uLCB3bWVhbl9maXRuZXNzKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29uZGl0aW9uLCB2YWx1ZXNfZnJvbSA9IHdtZWFuX2ZpdG5lc3MpICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAibG9jdXMiKSAlPiUgYXMubWF0cml4ICU+JQogIGRpc3QKCnBsb3RfY2x1c3Rlcl9kZW5kIDwtIGRpc3RfaGVhdG1hcCAlPiUKICBoY2x1c3QobWV0aG9kID0gIndhcmQuRDIiKSAlPiUgYXMuZGVuZHJvZ3JhbSAlPiUKICBzZXQoImJyYW5jaGVzX2tfY29sIiwgY3VzdG9tX2NvbG9yc1sxOjVdLCBrID0gNSkgJT4lCiAgc2V0KCJicmFuY2hlc19sd2QiLCAwLjUpICU+JQogIGFzLmdnZGVuZCAlPiUKICBnZ3Bsb3QobGFiZWxzID0gRkFMU0UpCgojIGFycmFuZ2UgYm90aCBvbiBzYW1lIHBsb3QKZ2dhcnJhbmdlKG5yb3cgPSAyLCBoZWlnaHRzID0gIGMoMC41LCAwLjUpLAogIHBsb3RfY2x1c3Rlcl9kZW5kICsgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC4xLCAwLjA5LCAtMC4xNSwgMC4xMzYpLCJucGMiKSksCiAgcGxvdF9oZWF0bWFwX3NpZwopCmBgYAoKIyMgR2VuZSBzaW1pbGFyaXR5IGJ5IGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBtZXRob2RzCgpXZSB1c2UgdHdvIGRpZmZlcmVudCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gbWV0aG9kcywgKipuTURTKiogYW5kICoqdC1TTkUqKi4gV2UgY2FuIGNoZWNrIGlmIHRoZXNlIG1ldGhvZHMgcmVwcm9kdWNlIHRoZSBjbHVzdGVyaW5nIGZvciB0aGUgc2lnbmlmaWNhbnRseSByZWd1bGF0ZWQgZ2VuZXMgcHJvZHVjZWQgd2l0aCBgaGNsdXN0YC4gQW5hbHlzaXMgc2hvd3MgdGhhdCB0aGUgc21hbGwgY2x1c3RlcnMgYXJlIG1vcmUgc3Ryb25nbHkgc2VwYXJhdGVkIGZyb20gdGhlIHJlc3QuCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDR9CiMgc2V0IGEgc2VlZCB0byBvYnRhaW4gc2FtZSBwYXR0ZXJuIGZvciBzdG9jaGFzdGljIG1ldGhvZHMKc2V0LnNlZWQoMTIzKQoKIyBydW4gbk1EUyBhbmFseXNpcwpOTURTIDwtICBkaXN0X2hlYXRtYXAgJT4lIG1ldGFNRFMKZGZfbm1kcyA8LSBOTURTJHBvaW50cyAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gImxvY3VzIikgJT4lCiAgbGVmdF9qb2luKGVuZnJhbWUobmFtZSA9ICJsb2N1cyIsIHZhbHVlID0gImNsdXN0ZXIiLAogICAgY3V0cmVlb3JkKGhjbHVzdChkaXN0X2hlYXRtYXAsIG1ldGhvZCA9ICJ3YXJkLkQyIiksIGsgPSA1KSkpCgojIHJ1biB0LVNORSBhbmFseXNpcwpTTkUgPC0gZGlzdF9oZWF0bWFwICU+JSB0c25lKG1heF9pdGVyID0gNTAwLCBwZXJwbGV4aXR5ID0gOCkKZGZfdHNuZSA8LSBTTkUgJT4lIHNldE5hbWVzKGMoIngiLCAieSIpKSAlPiUgYXNfdGliYmxlICU+JQogIG11dGF0ZShsb2N1cyA9IHVuaXF1ZShkZl9oZWF0bWFwJGxvY3VzKSkgJT4lCiAgbGVmdF9qb2luKGVuZnJhbWUobmFtZSA9ICJsb2N1cyIsIHZhbHVlID0gImNsdXN0ZXIiLAogICAgY3V0cmVlb3JkKGhjbHVzdChkaXN0X2hlYXRtYXAsIG1ldGhvZCA9ICJ3YXJkLkQyIiksIGsgPSA1KSkpCgpwbG90X25tZHMgPC0gZGZfbm1kcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBNRFMxLCB5ID0gTURTMiwgY29sb3IgPSBmYWN0b3IoY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKyBsYWJzKHRpdGxlID0gIm5NRFMiKSArCiAgY3VzdG9tX3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44NSwgMC43OCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9ycykKCnBsb3RfdHNuZSA8LSBkZl90c25lICU+JQogIGdncGxvdChhZXMoeCA9IFYxLCB5ID0gVjIsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgbGFicyh0aXRsZSA9ICJ0LVNORSIpICsKICBjdXN0b21fdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAwLjc4KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzKQoKZ2dhcnJhbmdlKG5jb2wgPSAyLCBwbG90X25tZHMsIHBsb3RfdHNuZSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bsb3Rfbm1kc190c25lLnN2ZyIsCiAgcGxvdCA9IGdnYXJyYW5nZShuY29sID0gMiwgcGxvdF9ubWRzLCBwbG90X3RzbmUpLAogIGRldmljZSA9ICJzdmciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDQpCmBgYAoKIyMgRml0IG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscwoKV2UgY2FuIGZpbmQgY2x1c3RlcnMgb2YgZ2VuZXMgd2l0aCBzaW1pbGFyIGZpdG5lc3MsIGJ1dCBpdCBpcyBhbHNvIGltcG9ydGFudCB0byBpZGVudGlmeSBfd2h5XyB0aGV5IGNsdXN0ZXIgdG9nZXRoZXIuIEluIG9yZGVyIHRvIGZpbmQgb3V0IF93aGljaCB2YXJpYWJsZXNfIGRldGVybWluZSB0aGUgZml0bmVzcyBvdXRjb21lIG9mIGEgZ2VuZSwgd2UgY2FuIHBlcmZvcm0gKiptdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbioqLiBFYWNoIGdlbmUgbmVlZHMgdG8gaGF2ZSBmaXRuZXNzIG91dGNvbWVzIGFubm90YXRlZCB3aXRoIHRoZSBkaWZmZXJlbnQgKG1peGVkKSB2YXJpYWJsZXMgYGNhcmJvbmAsIGBsaWdodGAsIGB0cmVhdG1lbnRgLiBUaGUgbGF0dGVyIGNhbiBiZSBzdWJkaXZpZGVkIGluIGluZGl2aWR1YWwgdHJlYXRtZW50IGNvbHVtbnMgZ2x1Y29zZSwgRENNVSwgZmx1Y3R1YXRpbmcgbGlnaHQsIGFuZCBzbyBvbi4gTXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gZml0cyBhIGxpbmVhciBtb2RlbCBvZiB0aGUgZm9sbG93aW5nIGZvcm0gdG8gdGhlIGRhdGE6CgpgcmVzcG9uc2UgfiBpbnRlcmNlcHQgKyBwcmVkaWN0b3IgQSB4IHNsb3BlIEEgKyBwcmVkaWN0b3IgQiB4IHNsb3BlIEIgeCAuLi5gCgpIZXJlLCBgZml0bmVzc2AgaXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLCB0aGUgZGlmZmVyZW50IGNvbmRpdGlvbnMgYXJlIHRoZSBwcmVkaWN0b3JzLiBJdCBpcyBpbXBvcnRhbnQgdG8gY29udmVydCB0aGUgY2F0ZWdvcmljYWwgcHJlZGljdG9ycyBpbnRvIChudW1lcmljYWwpIGR1bW15IHZhcmlhYmxlcy4gVGhlbiBmb3IgZWFjaCBpbmRpdmlkdWFsIGdlbmUsIG11bHRpcGxlIGxpbmVhciBtb2RlbHMgYXJlIGZpdHRlZCBhbmQgdGhlIHBvd2VyIG9mIGVhY2ggcHJlZGljdG9yIHZhcmlhYmxlIHRvIHByZWRpY3QgdGhlIHJlc3BvbnNlIGlzIGV4dHJhY3RlZC4KCmBgYHtyfQojIGZpeGVkIG1vZGVsIHdpdGggNiBwcmVkaWN0b3IgdmFyaWFibGVzIC0tIGR5bmFtaWMgbGF5b3V0IHdvdWxkIAojIGJlIGJldHRlciBpbiBmdXR1cmUKZml0X2xpbnJlZyA8LSBmdW5jdGlvbih5LCB4MSwgeDIsIHgzLCB4NCwgeDUsIHg2KXsKICBmaXQgPC0gbG0oeSB+IHgxICsgeDIgKyB4MyArIHg0ICsgeDUgKyB4NikKICBjKGNvZWZmaWNpZW50cyhmaXQpLCBzdW1tYXJ5KGZpdCkkY29lZmZpY2llbnRzWywgNF0sCiAgICBzdW1tYXJ5KGZpdCkkci5zcXVhcmVkKQp9CgojIHJlY29kZSBjYXRlZ29yaWNhbCB0byBudW1lcmljYWwgKGR1bW15KSB2YXJpYWJsZXMKZGZfbGlucmVnIDwtIGRmX2dlbmUgJT4lCiAgZmlsdGVyKCFpcy5uYShsb2N1cykpICU+JQogIHNlbGVjdChsb2N1cywgY2FyYm9uLCBsaWdodCwgdHJlYXRtZW50LCB3bWVhbl9maXRuZXNzKSAlPiUgZGlzdGluY3QgJT4lCiAgbXV0YXRlKAogICAgY2FyYm9uID0gcmVjb2RlKGNhcmJvbiwgYEhDYCA9IDEsIGBMQ2AgPSAwKSwKICAgIGxpZ2h0ID0gcmVjb2RlKGxpZ2h0LCBgTExgID0gMCwgYElMYCA9IDAuNSwgYEhMYCA9IDEpKSAlPiUKICBtdXRhdGUoZHVtbXkgPSAxLCB0cmVhdG1lbnQgPSByZXBsYWNlKHRyZWF0bWVudCwgdHJlYXRtZW50ID09ICIiLCAiLSIpKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHJlYXRtZW50LCB2YWx1ZXNfZnJvbSA9IGR1bW15LCB2YWx1ZXNfZmlsbCA9IDApICU+JQogIG11dGF0ZShgK0dgID0gYCtHYCArIGArRCwgK0dgKSAlPiUgcmVuYW1lKGArRGAgPSBgK0QsICtHYCkgJT4lIHNlbGVjdCgtYC1gKSAlPiUKICAjIGZpdCBtb2RlbAogIGdyb3VwX2J5KGxvY3VzKSAlPiUKICBzdW1tYXJpemUoY29lZmZpY2llbnQgPSBmaXRfbGlucmVnKHdtZWFuX2ZpdG5lc3MsIGNhcmJvbiwgbGlnaHQsIGAtTmAsIGArRkxgLCBgK0dgLCBgK0RgKSwKICAgIC5ncm91cHMgPSAia2VlcCIpICU+JSAjdW5uZXN0KGNvZWZmaWNpZW50KSAlPiUKICBtdXRhdGUodHJlYXRtZW50ID0gYyhyZXAoYygiaW50ZXJjZXB0IiwgImNhcmJvbiIsICJsaWdodCIsICItTiIsICIrRkwiLCAiK0ciLCAiK0QiKSwgMikgJT4lIAogICAgcGFzdGUwKHJlcChjKCIiLCAicHZhbF8iKSwgZWFjaCA9IDcpLCAuKSwgInJfc3F1YXJlZCIpKQpgYGAKCk5vdyB3ZSBjYW4gb3ZlcmxheSB0aGUgaW5mb3JtYXRpb24gb2YgdGhlIGJlc3QgcHJlZGljdG9yIHZhcmlhYmxlIG9uIHRoZSBjbHVzdGVyIG1hcCBwcm9kdWNlZCBieSB0U05FLCBmb3IgZXhhbXBsZSwgYW5kIHRoaXMgd2F5IGlkZW50aWZ5IGdyb3VwcyBvZiBnZW5lcyByZWd1bGF0ZWQgaW4gYSBzaW1pbGFyIGRlZ3JlZSwgYnkgc2ltaWxhciB2YXJpYWJsZXMuCgpgYGB7ciwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3RzbmVfbGlucmVnIDwtIGRmX3RzbmUgJT4lCiAgaW5uZXJfam9pbihkZl9saW5yZWcsIGJ5ID0gImxvY3VzIikgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZl9nZW5lLCBsb2N1cywgc2dSTkFfdGFyZ2V0KSAlPiUgZGlzdGluY3QsIGJ5ID0gImxvY3VzIikgJT4lCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHRyZWF0bWVudCwgImludGVyY2VwdHxwdmFsfHJfc3F1YXJlZCIpKSAlPiUKICBtdXRhdGUoc2dSTkFfdGFyZ2V0ID0gaWZfZWxzZShhYnMoY29lZmZpY2llbnQpID4gMiwgc2dSTkFfdGFyZ2V0LCAiIikpICU+JQogIG11dGF0ZShwb2ludF9zaXplID0gYWJzKGNvZWZmaWNpZW50KSwKICAgIGNvZWZmaWNpZW50ID0gY29lZmZpY2llbnQgJT4lIHJlcGxhY2UoLiwgLiA+IDUsIDUpICU+JSByZXBsYWNlKC4sIC4gPCAtNSwgLTUpKSAlPiUKICAKICBnZ3Bsb3QoYWVzKHggPSBWMSwgeSA9IFYyLCBzaXplID0gcG9pbnRfc2l6ZSwKICAgIGNvbG9yID0gY29lZmZpY2llbnQsIGxhYmVsID0gc2dSTkFfdGFyZ2V0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJ0LVNORSBjbHVzdGVyaW5nIG9mIERGIGdlbmVzIiwgCiAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiZG90IGNvbG9yL3NpemUgZW5jb2RlcyBlZmZlY3Qgb2YgdmFyaWFibGUsIG4gPSAiLCBucm93KGRmX3RzbmUpKSkgKwogIGN1c3RvbV90aGVtZShhc3BlY3QgPSAxKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGxpbWl0cyA9IGMoLTUsIDUpLAogICAgY29sb3VycyA9IGMoY3VzdG9tX2NvbG9yc1sxXSwgZ3JleSgwLjYsIDAuOCksIGN1c3RvbV9jb2xvcnNbMl0pKSArCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygxLCA2KSkgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMywgbWF4Lm92ZXJsYXBzID0gNTApICsKICBmYWNldF93cmFwKCB+IHRyZWF0bWVudCwgbmNvbCA9IDIpCgpwcmludChwbG90X3RzbmVfbGlucmVnKQpgYGAKClRoaXMgc3RyYXRlZ3kgcmV2ZWFscyBhIGxpc3Qgb2YgaW50ZXJlc3RpbmcgY29uZGl0aW9uLXNwZWNpZmljIGdlbmVzOgoKLSBOaXRyb2dlbiBsaW1pdGF0aW9uOiBgc3NyMzUzMmAgLSB1bmtub3duIHNob3J0IHByb3RlaW4sIHN0cm9uZ2VzdCBrbm93biBpbnRlcmFjdGlvbiBpbiBTVFJJTkcgd2l0aCBHbHNBIGdsdXRhbWluYXNlCi0gRmx1Y3R1YXRpbmcgbGlnaHQ6CiAgLSBgc2xsMTUyMWAgLSBQdXRhdGl2ZSBkaWZsYXZpbiBmbGF2b3Byb3RlaW4gQTMgKGRmYTMpLCBuZWdhdGl2ZWx5IGNvcnIuIHdpdGggZml0bmVzcwogIC0gYHNsbDAyMTdgIFB1dGF0aXZlIGRpZmxhdmluIGZsYXZvcHJvdGVpbiBBMiAoZGZhMiksIHBvc2l0aXZlbHkgY29yci4gd2l0aCBmaXRuZXNzCi0gTWl4b3Ryb3BoeToKICAtIGBzbGwwNTkzYCAtIGdsaywgZ2x1Y29raW5hc2UsIGNhdGFseXplcyBQLXlsYXRpb24gb2YgR2xjIHRvIEc2UAogIC0gYHNsbDE1MzNgIC0gcGlsVCwgZmltYnJpYSBhc3NlbWJseSwgbW9iaWxpdHksIEdsYyB0cmFuc3BvcnQgb3Igc2Vuc2luZz8KICAtIGBzc2wzMzY0YCAtIHVua25vd24gc2hvcnQgcHJvdGVpbiwgc3Ryb25nbHkgaW50ZXJhY3RzIHdpdGggUmJjWCwgUmJjUiwgUHJrLiBJbXBvcnRhbnQgZm9yIEMtbWV0YWJvbGlzbSBhZGFwYXRpb24/Ci0gTGlnaHQ6CiAgLSBgc3NyMjE0MmAgeWNmMTksIHNob3J0IHVua25vd24gcHJvdGVpbiwgaW50ZXJhY3RzIHdpdGggcHNiTyBhbmQgVGF0IG1lbWJyYW5lIHByb3RlaW4gaW5zZXJ0aW9uIHN5c3RlbSwKICAtIGBzbHIwOTYzYCBzaXIsIHN1bGZpdGUgcmVkdWN0YXNlLCBmZXJyZWRveGluIEgyTyArIEhTICsgZmVycmVkb3hpbiA8LT4gSCsgKyByZWR1Y2VkIGZlcnJlZG94aW4gKyBzdWxmaXRlLAogICAgc3Ryb25nbHkgaW50ZXJhY3RzIHdpdGggb3RoZXIgcHJvdGVpbnMgaW4gc3VsZnVyIG1ldGFib2xpc20sIHNwZWNpZmljYWxseSByZWxhdGVkIHRvIGNvZmFjdG9yIGJpb3N5bnRoZXNpcywgCiAgICBjb2JhbGFtaW4gKHZpdGFtaW4gQjEyKSBhbmQgICAgc2lyb2hlbWUKLSBMaWdodCwgbWl4b3Ryb3BoeSwgaGV0ZXJvdHJvcGh5OiBjbHVzdGVyIG9mIHBob3Rvc3ludGhlc2lzIHJlbGF0ZWQgZ2VuZXMgaW5jcmVhc2UgZml0bmVzcyB3aGVuIEtPZWQ6IGFwY0EsRCxFLCBwc2JCLEMsRAotIENhcmJvbjoKICAtIGBzbGwwMjE3YCBQdXRhdGl2ZSBkaWZsYXZpbiBmbGF2b3Byb3RlaW4gQTIgKGRmYTIpLCBLTyBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCBmaXRuZXNzIHdpdGggQywgcG9zaXRpdmUgd2l0aCArRkwKICAtIGBzbGwwMjE4YCBzYW1lIGJlaGF2aW9yIGFzIGRmYTIsIGludGVyYWN0cyB3aXRoIGRmYTIsNCwgY29udHJpYnV0ZXMgdG8gUFNJSSBzdGFiaWxpemF0aW9uLCAKICAgICBbQmVyc2FuaW5pIGV0IGFsLiwgMjAxN10oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8yNzkyODgyNC8pCgojIyBMaXN0IG9mIGdlbmVzIHdpdGggc3Ryb25nIGZpdG5lc3MgY29ycmVsYXRpb24KClRoZSB0YWJsZSB3aXRoIGxpbmVhciByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgcC12YWx1ZXMgaXMgcmVzaGFwZWQgdG8gbG9uZyBmb3JtYXQgZm9yIGJldHRlciByZWFkYWJpbGl0eS4gVGhlIGBrYWJsZUV4dHJhYCBwYWNrYWdlIGlzIHVzZWQgdG8gY29sb3IgY2VsbHMgZm9yIGVhc2llciByZWNvZ25pdGlvbi4gVGhlbiB3ZSBzdWJzZXQgdGhlIHRhYmxlIGZvciBlYWNoIHRyZWF0bWVudCBpbiBvcmRlciB0byBzcG90IHRoZSBtb3N0IGludGVyZXN0aW5nIGdlbmVzLgoKYGBge3J9CmRmX2xpbnJlZ193aWRlIDwtIGRmX2xpbnJlZyAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHJlYXRtZW50LCB2YWx1ZXNfZnJvbSA9IGNvZWZmaWNpZW50KSAlPiUKICBsZWZ0X2pvaW4oc2VsZWN0KGRmX2dlbmUsIGxvY3VzLCBzZ1JOQV90YXJnZXQpICU+JSBkaXN0aW5jdCwgYnkgPSAibG9jdXMiKSAlPiUKICBzZWxlY3QoLW1hdGNoZXMoImludGVyY2VwdCIpKSAlPiUKICBmaWx0ZXIoaWZfYW55KG1hdGNoZXMoIl4oY2FyYnxsaWdodHxcXC18XFwrKSIpLCB+IGFicyguKSA+IDIpKSAlPiUKICBtdXRhdGUoYWNyb3NzKG1hdGNoZXMoImNhcmJ8bGlnaHR8XFwtfFxcKyIpLCB+IHJvdW5kKC4sIDMpKSkgJT4lIAogIHVuZ3JvdXAgJT4lIHNlbGVjdChzZ1JOQV90YXJnZXQsIGxvY3VzLCBtYXRjaGVzKCIuIikpCgpjb2xvcl90YWJsZSA8LSBmdW5jdGlvbihkZiwgdmFyaWFibGUpIHsKICBmaWx0ZXIoZGYsIGFicyguZGF0YVtbdmFyaWFibGVdXSkgPiAyKSAlPiUgCiAgc2VsZWN0KG1hdGNoZXMoIl4oc2d8bG9jfHJfc3xjYXJifGxpZ2h0fFxcLXxcXCspIikgfCBhbGxfb2YocGFzdGUwKCJwdmFsXyIsIHZhcmlhYmxlKSkpICU+JQogIGFycmFuZ2UoZGVzYyguZGF0YVtbdmFyaWFibGVdXSkpICU+JQogIG11dGF0ZShhY3Jvc3MoMzo4LCB+IGNlbGxfc3BlYyguLCAiaHRtbCIsIGNvbG9yID0gIndoaXRlIiwKICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IoLiwgb3B0aW9uID0gIkUiLCBzY2FsZSA9IGMoLTUuNSwgNS41KSksCiAgICAgIGJvbGQgPSBUUlVFKSkpICU+JQogIGtibChmb3JtYXQgPSAiaHRtbCIsIGVzY2FwZSA9IEYpICU+JQogIGthYmxlX3BhcGVyKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYpCn0KYGBgCgoKYGBge3J9CmRmX2xpbnJlZ193aWRlICU+JSBjb2xvcl90YWJsZSgiY2FyYm9uIikKYGBgCgpgYGB7cn0KZGZfbGlucmVnX3dpZGUgJT4lIGNvbG9yX3RhYmxlKCJsaWdodCIpCmBgYAoKYGBge3J9CmRmX2xpbnJlZ193aWRlICU+JSBjb2xvcl90YWJsZSgiLU4iKQpgYGAKCmBgYHtyfQpkZl9saW5yZWdfd2lkZSAlPiUgY29sb3JfdGFibGUoIitGTCIpCmBgYAoKYGBge3J9CmRmX2xpbnJlZ193aWRlICU+JSBjb2xvcl90YWJsZSgiK0ciKQpgYGAKCmBgYHtyfQpkZl9saW5yZWdfd2lkZSAlPiUgY29sb3JfdGFibGUoIitEIikKYGBgCkJhc2VkIG9uIHRoZSBtdWx0aXBsZSBsaW5lYXIgbW9kZWwgY29ycmVsYXRpb25zLCB3ZSBjYW4gdHJ5IHRvIGV4dHJhY3QgYSBzaG9ydGxpc3Qgb2YgdGhlIG1vc3QgaW50ZXJlc3RpbmcgKipoeXBvdGhldGljYWwgZ2VuZXMqKi4gVGhlc2UgY291bGQgd2FycmFudCBmdXJ0aGVyIGludmVzdGlnYXRpb25zLgoKYGBge3J9Cmxpc3RfdG9wX3Vua25vd25faGl0cyA8LSBkZl9saW5yZWdfd2lkZSAlPiUKICBsZWZ0X2pvaW4oZGZfdW5pcHJvdCwgYnkgPSAibG9jdXMiKSAlPiUKICAjIGZpbHRlciBieSBuYW1lOiBvbmx5IHVua25vd24gcHJvdGVpbnMKICBmaWx0ZXIoCiAgICBpcy5uYShnZW5lX25hbWVfc2hvcnQpLAogICAgc3RyX2RldGVjdChwcm90ZWluLCAiW2EtekEtWl17M31bMC05XXs0fSBwcm90ZWlufFVuY2hhcmFjdGVyaXplZCIpKSAlPiUKICAjIGZpbHRlciBieSBlZmZlY3Q6IG9ubHkgY29ycmVsYXRpb24gPiAzCiAgZmlsdGVyKGlmX2FueShtYXRjaGVzKCJeKGNhcmJ8bGlnaHR8XFwtfFxcKykiKSwgfiBhYnMoLikgPiAzKSkgJT4lCiAgYXJyYW5nZShkZXNjKHJfc3F1YXJlZCkpICU+JQogIHB1bGwobG9jdXMpCgpkZl9saW5yZWdfd2lkZSAlPiUgZmlsdGVyKGxvY3VzICVpbiUgbGlzdF90b3BfdW5rbm93bl9oaXRzKSAlPiUKICBzZWxlY3QoIXN0YXJ0c193aXRoKCJwdmFsIiksIC1zZ1JOQV90YXJnZXQpICU+JQogIG11dGF0ZShhY3Jvc3MoMjo3LCB+IGNlbGxfc3BlYyguLCAiaHRtbCIsIGNvbG9yID0gIndoaXRlIiwKICAgICAgYmFja2dyb3VuZCA9IHNwZWNfY29sb3IoLiwgb3B0aW9uID0gIkUiLCBzY2FsZSA9IGMoLTUuNSwgNS41KSksCiAgICAgIGJvbGQgPSBUUlVFKSkpICU+JQogIGtibChmb3JtYXQgPSAiaHRtbCIsIGVzY2FwZSA9IEYpICU+JQogIGthYmxlX3BhcGVyKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYpCmBgYAoKCiMjIEV4dHJhY3QgYW5kIGFuYWx5emUgaW50ZXJlc3RpbmcgZ2VuZSBjbHVzdGVycwoKVGhlIGxpc3QgYWJvdmUgc2hvd3MgdGhlIGdlbmVzIHdob3NlIGZpdG5lc3MgaXMgbW9zdCBzaWduaWZpY2FudGx5IGNvcnJlbGF0ZWQgd2l0aCBvbmUgb2YgdGhlIHRyZWF0bWVudHMuClRoaXMgbGlzdCBvZiBnZW5lcyBpcyBleHRyYWN0ZWQgYW5kIHRoZW4gc2ltcGx5IGZpdG5lc3MgcGVyIGNvbmRpdGlvbiBpcyBwbG90dGVkIGFzIGEgaGVhdG1hcCwgaW4gb3JkZXIgdG8gY29uZmlybSB0aGUgdHJlbmRzIGZyb20gZml0dGluZyB0aGUgbXVsdGlwbGUgbGluZXIgcmVncmVzc2lvbiBtb2RlbHMuCgoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpwbG90X3NnUk5Bc19saWdodCA8LSBkZl9nZW5lICU+JQogIGZpbHRlcihsb2N1cyAlaW4lIGxpc3RfdG9wX3Vua25vd25faGl0cywgdGltZSA9PSAwKSAlPiUKICBtdXRhdGUoc2dSTkFfdGFyZ2V0ID0gZmN0X2NsdXN0ZXIoc2dSTkFfdGFyZ2V0LCBjb25kaXRpb24sIHdtZWFuX2ZpdG5lc3MpKSAlPiUKICBtdXRhdGUoY29uZGl0aW9uID0gZmN0X2NsdXN0ZXIoY29uZGl0aW9uLCBzZ1JOQV90YXJnZXQsIHdtZWFuX2ZpdG5lc3MpKSAlPiUKICBtdXRhdGUod21lYW5fZml0bmVzcyA9IHdtZWFuX2ZpdG5lc3MgJT4lIHJlcGxhY2UoLiwgLiA+IDQsIDQpICU+JSByZXBsYWNlKC4sIC4gPCAtNCwgLTQpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBzZ1JOQV90YXJnZXQsIGZpbGwgPSB3bWVhbl9maXRuZXNzKSkgKwogIGdlb21fdGlsZSgpICsgY3VzdG9tX3RoZW1lKCkgKwogIGxhYnModGl0bGUgPSAiVG9wIHVua25vd24gZ2VuZXMiLCB4ID0gIiIsIHkgPSAiIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoY3VzdG9tX2NvbG9yc1sxXSwgZ3JleSgwLjkpLCBjdXN0b21fY29sb3JzWzJdKSwKICAgIGxpbWl0cyA9IGMoLTQsIDQpKQoKcHJpbnQocGxvdF9zZ1JOQXNfbGlnaHQpCnNhdmVfcGxvdChwbG90X3NnUk5Bc19saWdodCwgd2lkdGggPSA4LjAsIGhlaWdodCA9IDMuNSkKYGBgCioqU3VtbWFyeSoqCgotIGBzbGwwMzY0YCAtIDEzOSBBQS4gS0QgaGFzIGhpZ2hlciBmaXRuZXNzIGluIEhDIGNvbmRpdGlvbnMgYW5kIGxvd2VyIGZpdG5lc3MgaW4gSEwuIE5lZ2F0aXZlbHkgcmVndWxhdGluZyBjYXJib24gbWV0YWJvbGlzbT8KLSBgc2xsMDQ4MWAgLSAxNTUgQUEuIEtEIGhhcyBoaWdoZXIgZml0bmVzcyBpbiArRyBjb25kaXRpb25zIGFuZCBsb3dlciBmaXRuZXNzIGluIEhMLiBNZW1icmFuZSBsb2NhbGl6YXRpb24uIE5lZ2F0aXZlbHkgcmVndWxhdGluZyBnbHljb2x5c2lzPwotIGBzbGwwODc3YCAtIDQ1NiBBQS4gS0QgaGFzIGhpZ2hlciBmaXRuZXNzIF9vbmx5XyBpbiBIQyxMTC4gTWl0aWdhdGVzIGxpZ2h0IGxpbWl0YXRpb24/Ci0gYHNzbDMzNjRgIC0gIDc0IEFBLiBLRCBoYXMgbG93ZXIgZml0bmVzcyBvbiBhbGwgSEMvK0cgY29uZGl0aW9ucy4gVGhpcyBwcm90ZWluIGlzIGtub3duIGFzIENQMTIgcHJvdGVpbiwgcmVndWxhdGluZyBnbHljb2x5dGljIGZsdXggYXQgR0FQREggYW5kIFBSSy4KLSBgc3NyMzUzMmAgLSAgODAgQUEuIEtEIGxvd2VyIGZpdG5lc3Mgb24gTi1saW1pdGF0aW9uIGFuZCBDLWxpbWl0YXRpb24gKExDLUhMIGNvbWJpbmF0aW9ucykuIFNhbWUgb3Blcm9uIGFzIGdsdXRhbWluYXNlIGdsc0EgKHNscjIwNzksIGNhdGFseXplcyBkZWFtaW5hdGlvbiBvZiBnbG4gLS0+IGdsdSksIHJlZ3VsYXRvcnksIGludm9sdmVkIGluIE4gbWV0YWJvbGlzbT8KLSBgc2xyMTk5MGAgLSAyNDAgQUEsIDUgVE0gZG9tYWlucy4gS0QgaGlnaGVyIGZpdG5lc3MgaW4gcGhvdG9oZXRlcm90cm9waHksIGxvd2VyIGZpdG5lc3MgaW4gYWxsIEhDL0xMIGNvbmRpdGlvbnMuIFNvbWV0aGluZyBpbXBvcnRhbnQgZm9yIHBob3Rvc3lzdGVtcz8gU29tZXRoaW5nIHRoYXQgd2FzdGVzIGUtIGluIHBob3RvaGV0ZXJvdHJvcGhpYyBjb25kaXRpb25zPwotIGBzbGw2MDU1YCAtIDE1MiBBQS4gRml0bmVzcyBwcm9maWxlIGFzIGFib3ZlLiBNdWx0aXViaXF1aXRpbiBkb21haW4sIGludm9sdmVkIGluIHByb3RlaW4gbW9kaWZpY2F0aW9uL2RlZ3JhZGF0aW9uIG9mIFBTIHByb3RlaW5zPwotIGBzbHIxNTA1YCAtIDE5OCBBQS4gRml0bmVzcyBwcm9maWxlIGFzIGFib3ZlLiBObyB1c2VmdWwgaW5mb3JtYXRpb24uCi0gYHNsbDEzNzhgIC0gMzAwIEFBLiAgS0QgaGFzIGxvd2VyIGZpdG5lc3Mgb24gYWxsIExMIGNvbmRpdGlvbnMuIE1lbWJyYW5lIGFzc29jaWF0ZWQgcHJvdGVpbj8gSW4gU1RSSU5HLCBwb3RlbnRpYWwgaW50ZXJhY3Rpb24gd2l0aCBQYnNBMSBhbmQgUGJzQTIgKEhlbWUgb3h5Z2VuYXNlIDEgYW5kIDIpLiBQb3RlbnRpYWxseSBpbXBvcnRhbnQgZm9yIGNobG9yb3BoeWxsIG9yIGhlbWUgYmlvc3ludGhlc2lzIC0tPiB3b3VsZCBleHBsYWluIGltcG9ydGFuY2UgZm9yIHBob3Rvc3ludGhlc2lzIGluIExMIGNvbmRpdGlvbi4KLSBgc2xyMTEwMmAgLSA4NTMgQUEuIEtEIGhhcyBsb3dlciBmaXRuZXNzIG9uIGFsbCBMTCBjb25kaXRpb25zLiA0IGtub3duIGRvbWFpbnMsIEZIQSAoZm9ya2hlYWQtYXNzb2NpYXRlZCBkb21haW4gaXMgYSBwaG9zcGhvcGVwdGlkZSByZWNvZ25pdGlvbiBkb21haW4gZm91bmQgaW4gbWFueSByZWd1bGF0b3J5IHByb3RlaW5zKSwgUEFTIChzaWduYWxpbmcsIG9mdGVuIGludm9sdmVkIGluIGNpcmNhZGlhbiBwcm90ZWlucywgZGV0ZWN0IHRoZWlyIHNpZ25hbCBieSB3YXkgb2YgYW4gYXNzb2NpYXRlZCBjb2ZhY3RvciBsaWtlIGhlbWUsIGZsYXZpbiksIEdHREVGIChpbnZvbHZlZCBpbiBzaWduYWwgdHJhbnNkdWN0aW9uLCBsaWtlbHkgdG8gY2F0YWx5emUgc3ludGhlc2lzIG9yIGh5ZHJvbHlzaXMgb2YgY3ljbGljIGRpZ3VhbnlsYXRlIGMtZGlHTVApLCBFQUwgKHNob3duIHRvIHN0aW11bGF0ZSBkZWdyYWRhdGlvbiBvZiBhIHNlY29uZCBtZXNzZW5nZXIsIGN5Y2xpYyBkaS1HTVAsIGNhbmRpZGF0ZSBmb3IgYSBkaWd1YW55bGF0ZSBwaG9zcGhvZGllc3RlcmFzZSBmdW5jdGlvbi4gVG9nZXRoZXIgd2l0aCB0aGUgR0dERUYgZG9tYWluLCBFQUwgbWlnaHQgYmUgaW52b2x2ZWQgaW4gcmVndWxhdGluZyBjZWxsIHN1cmZhY2UgYWRoZXNpdmVuZXNzIGluIGJhY3RlcmlhKS4gU291cmNlOiBJbnRlclByby4gRW1iZWRkZWQgaW4gYSBbdGlnaHQgbmV0d29ya10oaHR0cHM6Ly9zdHJpbmctZGIub3JnL25ldHdvcmsvMTE0OC4xNjUxODM0KSBvZiBpbnRlcmFjdGluZyBwcm90ZWlucyBhbGwgaW52b2x2ZWQgaW4gY2hyb21vcGhvcmUgYmlvc3ludGhlc2lzL21hdHVyYXRpb24uCgoqKkFwYyBhbmQgY3BjIHJlcHJlc3Npb24gbXV0YW50cyoqIGVuY29kaW5nIHBoeWNvYmlsaXNvbWVzIGFyZSBhbHNvIGVucmljaGVkIGluIGhpZ2ggbGlnaHQKCgpgYGB7ciwgZmlnLndpZHRoID0gMy41LCBmaWcuaGVpZ2h0ID0gNC41fQpwbG90X3NnUk5Bc19waHljb2JpbCA8LSBkZl9nZW5lICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KGdlbmVfbmFtZSwgIlthY11wYyIpLCB0aW1lID09IDApICU+JQogIG11dGF0ZSh3bWVhbl9maXRuZXNzID0gd21lYW5fZml0bmVzcyAlPiUgcmVwbGFjZSguLCAuID4gNCwgNCkgJT4lIHJlcGxhY2UoLiwgLiA8IC00LCAtNCkpICU+JQogIGdncGxvdChhZXMoeCA9IGNvbmRpdGlvbiwgeSA9IGZjdF9yZXYoc2dSTkFfdGFyZ2V0KSwgZmlsbCA9IHdtZWFuX2ZpdG5lc3MpKSArCiAgZ2VvbV90aWxlKCkgKyBjdXN0b21fdGhlbWUoKSArCiAgbGFicyh0aXRsZSA9ICJBcGMvQ3BjIHJlcHJlc3Npb24gbXV0YW50cyIsIHggPSAiIiwgeSA9ICIiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gYyhjdXN0b21fY29sb3JzWzFdLCBncmV5KDAuOSksIGN1c3RvbV9jb2xvcnNbMl0pLAogICAgbGltaXRzID0gYygtNCwgNCkpCgpwcmludChwbG90X3NnUk5Bc19waHljb2JpbCkKc2F2ZV9wbG90KHBsb3Rfc2dSTkFzX3BoeWNvYmlsLCB3aWR0aCA9IDYuNSwgaGVpZ2h0ID0gMy41KQpgYGAKCgojIERpcmVjdCBjb21wYXJpc29uIG9mIGdlbmUgZml0bmVzcwoKIyMgRml0bmVzcyBvZiBhbGwgY29uZGl0aW9ucyB2cyBlYWNoIG90aGVyCgpXZSBjYW4gcGxvdCBzZWxlY3RlZCBjb25kaXRpb25zIGFnYWluc3QgZWFjaCBvdGhlciBhbmQgYWRkIGdlbmUgbGFiZWxzIGluIG9yZGVyIHRvIGZpbmQgb3IgY29uZmlybSBwYXJ0aWN1bGFyIHBhdHRlcm5zLgoKYGBge3J9Cm1ha2VfZml0bmVzc19wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIHZhcnMsIHRpdGxlID0gTlVMTCkgewogICMgcHJlcGFyZSBkYXRhIGZvciB0d28gIHZhcmlhYmxlcyBlYWNoCiAgZGF0YSAlPiUgdW5ncm91cCAlPiUKICAgIGZpbHRlcihjb25kaXRpb24gJWluJSB2YXJzLCBzZ1JOQV90eXBlID09ICJnZW5lIikgJT4lCiAgICBzZWxlY3QobG9jdXMsIHNnUk5BX3RhcmdldCwgY29uZGl0aW9uLCB3bWVhbl9maXRuZXNzKSAlPiUgZGlzdGluY3QgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29uZGl0aW9uLCB2YWx1ZXNfZnJvbSA9IHdtZWFuX2ZpdG5lc3MpICU+JQogICAgbXV0YXRlKAogICAgICBkZml0ID0gZ2V0KHZhcnNbMV0pIC0gZ2V0KHZhcnNbMl0pLAogICAgICBzaWduaWZpY2FudCA9ICFiZXR3ZWVuKGRmaXQsIHF1YW50aWxlKGRmaXQsIHByb2JzID0gYygwLjAwMykpLAogICAgICAgIHF1YW50aWxlKGRmaXQsIHByb2JzID0gYygwLjk5NykpKSwKICAgICAgc2dSTkFfdGFyZ2V0ID0gaWZfZWxzZShzaWduaWZpY2FudCwgc2dSTkFfdGFyZ2V0LCAiIikpICU+JQogICAgCiAgICAjIHBsb3QKICAgIGdncGxvdChhZXMoeCA9IGdldCh2YXJzWzFdKSwgeSA9IGdldCh2YXJzWzJdKSwgCiAgICAgIGNvbG9yID0gc2lnbmlmaWNhbnQsIGxhYmVsID0gc2dSTkFfdGFyZ2V0KSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMSkgKyBjdXN0b21fdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gMCkgKwogICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDIsIHNpemUgPSAwLjgpICsKICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDQsIHNsb3BlID0gMSwgY29sID0gZ3JleSgwLjUpLCBsdHkgPSAyLCBzaXplID0gMC44KSArCiAgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtNCwgc2xvcGUgPSAxLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDIsIHNpemUgPSAwLjgpICsKICAgIGdlb21fdGV4dF9yZXBlbChzaXplID0gMywgbWF4Lm92ZXJsYXBzID0gNTApICsKICAgIGxhYnModGl0bGUgPSB0aXRsZSwgeCA9IHZhcnNbMV0sIHkgPSB2YXJzWzJdKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTksIDUpLCB5bGltID0gYygtOSwgNSkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGdyZXkoMC41KSwgY3VzdG9tX2NvbG9yc1syXSkpCn0KCiMgYnJvd3NlIHRocm91Z2ggYWxsIHBvc3NpYmxlIGNvbmRpdGlvbiBjb21iaW5hdGlvbnM7CiMgd2UgbmVlZCBhIGhlbHBlciBmdW5jdGlvbiB0aGF0IGRldGVjdHMgZHVwbGljYXRlZCBjb21iaW5hdGlvbnMKZHVwbGljYXRlZF8ydmVjIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICB4eSA9IHBhc3RlKHgsIHkpOyB5eCA9IHBhc3RlKHksIHgpCiAgc2FwcGx5KHh5LCBmdW5jdGlvbih4dmFsKSB7CiAgICB3aGljaCh4dmFsID09IHl4KSA8PSB3aGljaCh4dmFsID09IHh5KQogIH0pCn0KCmxpc3RfY29uZGl0aW9uX3BhaXJzIDwtIGxhcHBseSgKICB1bmlxdWUoZGZfZ2VuZSRjb25kaXRpb24pICU+JSBleHBhbmRfZ3JpZCh4ID0gLiwgeSA9IC4pICU+JQogICAgZmlsdGVyKCFkdXBsaWNhdGVkXzJ2ZWMoeCwgeSkpICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSBhcy5saXN0LAogIGZ1bmN0aW9uKHZhcikgewogICAgbWFrZV9maXRuZXNzX3Bsb3QoZGZfZ2VuZSwgdmFycyA9IHZhciwKICAgICAgdGl0bGUgPSBwYXN0ZSh2YXIsIGNvbGxhcHNlID0gIiAgLSAgIikpCiAgfQopCgojIGV4cG9ydCBpbWFnZXMKaW52aXNpYmxlKGNhcHR1cmUub3V0cHV0KAogIGxhcHBseShsaXN0X2NvbmRpdGlvbl9wYWlycywgZnVuY3Rpb24ocGwpIHsKICAgIHBsX25hbWUgPC0gcGFzdGUwKCIuLi9maWd1cmVzL3BhaXJ3aXNlX2NvbXBhcmlzb25zL3Bsb3RfIiwgcGwkbGFiZWxzJHgsICJfIiwgcGwkbGFiZWxzJHksICIucG5nIikKICAgIHBuZyhmaWxlbmFtZSA9IHBsX25hbWUsIHdpZHRoID0gODAwLCBoZWlnaHQgPSA4MDAsIHJlcyA9IDEyMCkKICAgIHByaW50KHBsKQogICAgZGV2Lm9mZigpCiAgfSkKKSkKYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQojIGV4YW1wbGUgb2YgZmlyc3QgNCBjb21iaW5hdGlvbnMKbGlzdF9jb25kaXRpb25fcGFpcnNbMTo0XQpgYGAKCiMgRGlmZmVyZW50aWFsIGZpdG5lc3Mgb2Ygc2VsZWN0ZWQgZ2VuZSBzZXRzCgojIyBDZW50cmFsIGNhcmJvbiBtZXRhYm9saXNtCgpUbyBwbG90IGdlbmUgZml0bmVzcyBmb3IgdGhlIGVuenltZXMgb2YgY2VudHJhbCBjYXJib24gbWV0YWJvbGlzbSwgd2UgdXNlIHRoZSBjb21wbGV0ZSBsaXN0IG9mIGVuenltZXMgYW5kIHRoZSBnZW5lcyB0aGF0IHRoZXkgYXJlIG1hcHBlZCB0byAob2J0YWluZWQgZnJvbSBLRUdHKS4gV2UgY2FuIGV4dHJhY3QgZ2VuZSBzZXRzIGZvciBzcGVjaWZpYyBwYXRod2F5cyBhbmQgcGxvdCBmaXRuZXNzLiBXZSBzdGFydCB3aXRoIGdseWNvbHlzaXMgYW5kIENhbHZpbiBjeWNsZSBlbnp5bWVzLgoKYGBge3J9Cmxpc3RfY2VudHJhbF9tZXRfcGF0aHdheXMgPC0gYygKICAiR2x5Y29seXNpcyAvIEdsdWNvbmVvZ2VuZXNpcyIsCiAgIlBlbnRvc2UgcGhvc3BoYXRlIHBhdGh3YXkiLAogICJDYXJib24gZml4YXRpb24gaW4gcGhvdG9zeW50aGV0aWMgb3JnYW5pc21zIiwKICAiUGhvdG9zeW50aGVzaXMiLAogICJDaXRyYXRlIGN5Y2xlIChUQ0EgY3ljbGUpIiwKICAiUHlydXZhdGUgbWV0YWJvbGlzbSIsCiAgIkdseW94eWxhdGUgYW5kIGRpY2FyYm94eWxhdGUgbWV0YWJvbGlzbSIKKQpgYGAKCgpgYGB7cn0KcGxvdF9nZW5lX2ZpdG5lc3MgPC0gZnVuY3Rpb24oZGYsIHB3ID0gTlVMTCwgZ2VuZSA9IE5VTEwsCiAgdGl0bGUgPSBOVUxMLCBuY29sID0gOCwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpIHsKICBkZiA8LSBkZiAlPiUgZmlsdGVyKHRpbWUgPT0gMCkKICBpZiAoIWlzLm51bGwocHcpKSB7CiAgICBkZiA8LSBkZiAlPiUgaW5uZXJfam9pbihkZl9rZWdnICU+JSBmaWx0ZXIoa2VnZ19wYXRod2F5ID09IHB3KSAlPiUgc2VsZWN0KGxvY3VzKSwKICAgICAgYnkgPSAibG9jdXMiKQogICAgdGl0bGUgPC0gcHcKICB9IGVsc2UgaWYgKCFpcy5udWxsKGdlbmUpKSB7CiAgICBkZiA8LSBkZiAlPiUgZmlsdGVyKGxvY3VzICVpbiUgZ2VuZSkKICB9CiAgCiAgZ2dwbG90KGRmLCBhZXMoeCA9IGNvbmRpdGlvbiwgeSA9IHdtZWFuX2ZpdG5lc3MsIAogICAgeW1pbiA9IHdtZWFuX2ZpdG5lc3Mtc2RfZml0bmVzcywgCiAgICB5bWF4ID0gd21lYW5fZml0bmVzcytzZF9maXRuZXNzLCBmaWxsID0gY29uZGl0aW9uLCBjb2xvciA9IGNvbmRpdGlvbikpICsKICAgIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIiwgd2lkdGggPSAwLjYpICsKICAgIGdlb21fZXJyb3JiYXIocG9zaXRpb24gPSAiZG9kZ2UiLCB3aWR0aCA9IDAuNiwgc2l6ZSA9IDEpICsKICAgIGN1c3RvbV90aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICBsZWdlbmQucG9zaXRpb24gPSBsZWdlbmQucG9zaXRpb24sIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC40LCAiY20iKSkgKyAKICAgIGxhYnModGl0bGUgPSB0aXRsZSwgeCA9ICIiLCB5ID0gImZpdG5lc3MiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JSYW1wUGFsZXR0ZShjdXN0b21fY29sb3JzWzE6NV0pKDExKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9yUmFtcFBhbGV0dGUoY3VzdG9tX2NvbG9yc1sxOjVdKSgxMSkpICsKICAgIGZhY2V0X3dyYXAofiBzZ1JOQV90YXJnZXQsIG5jb2wgPSBuY29sLCBkcm9wID0gRkFMU0UpCn0KYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2fQpwcmludChwbG90X2dlbmVfZml0bmVzcyhkZl9nZW5lLCBwdyA9IGxpc3RfY2VudHJhbF9tZXRfcGF0aHdheXNbWzFdXSkpCmdnc2F2ZSgiLi4vZmlndXJlcy9wbG90X2ZpdG5lc3NfZ2x5Y29seXNpcy5zdmciLAogIHBsb3RfZ2VuZV9maXRuZXNzKGRmX2dlbmUsIHB3ID0gbGlzdF9jZW50cmFsX21ldF9wYXRod2F5c1tbMV1dKSwKICB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwcmludChwbG90X2dlbmVfZml0bmVzcyhkZl9nZW5lLCBwdyA9IGxpc3RfY2VudHJhbF9tZXRfcGF0aHdheXNbWzJdXSkpCmdnc2F2ZSgiLi4vZmlndXJlcy9wbG90X2ZpdG5lc3NfcGVudG9zZS5zdmciLAogIHBsb3RfZ2VuZV9maXRuZXNzKGRmX2dlbmUsIHB3ID0gbGlzdF9jZW50cmFsX21ldF9wYXRod2F5c1tbMl1dKSwKICB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwcmludChwbG90X2dlbmVfZml0bmVzcyhkZl9nZW5lLCBwdyA9IGxpc3RfY2VudHJhbF9tZXRfcGF0aHdheXNbWzNdXSkpCmdnc2F2ZSgiLi4vZmlndXJlcy9wbG90X2ZpdG5lc3NfY2FyYm9uZml4LnN2ZyIsCiAgcGxvdF9nZW5lX2ZpdG5lc3MoZGZfZ2VuZSwgcHcgPSBsaXN0X2NlbnRyYWxfbWV0X3BhdGh3YXlzW1szXV0pLAogIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0fQpwcmludChwbG90X2dlbmVfZml0bmVzcyhkZl9nZW5lLCBwdyA9IGxpc3RfY2VudHJhbF9tZXRfcGF0aHdheXNbWzVdXSkpCmdnc2F2ZSgiLi4vZmlndXJlcy9wbG90X2ZpdG5lc3NfY2l0cmF0ZS5zdmciLAogIHBsb3RfZ2VuZV9maXRuZXNzKGRmX2dlbmUsIHB3ID0gbGlzdF9jZW50cmFsX21ldF9wYXRod2F5c1tbNV1dKSwKICB3aWR0aCA9IDgsIGhlaWdodCA9IDQpCmBgYAoKIyMgR2VuZSBmaXRuZXNzIGluIG1peG90cm9waHkgYW5kIGhldGVyb3Ryb3BoeQoKVXNpbmcgW2ZsdWN0dWF0b3JdKGh0dHBzOi8vZ2l0aHViLmNvbS9tLWphaG4vZmx1Y3R1YXRvciksIHdlIGNhbiBpbXBvcnQgYSBjdXN0b20gbWV0YWJvbGljIG1hcCBmb3IgX1N5bmVjaG9jeXN0aXNfIHNwLiBQQ0MgNjgwMywgYW5kIG92ZXJsYXkgcHVibGlzaGVkIGZsdXhlcyB0aGF0IHdlcmUgbWVhc3VyZWQgd2l0aCBMQy1NUyB1c2luZyBpc290b3BpY2FsbHkgbGFiZWxsZWQgY2FyYm9uIHNvdXJjZXMgKFtOYWthamltYSBldCBhbC4sIDIwMTRdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDkzL3BjcC9wY3UwOTEpKS4KCkZsdWN0dWF0b3IgY2FuIGJlIGluc3RhbGxlZCB1c2luZyBhIGZ1bmN0aW9uIGZyb20gYGRldnRvb2xzYDoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibS1qYWhuL2ZsdWN0dWF0b3IiKQpgYGAKCldlIGltcG9ydCB0aGUgbWV0YWJvbGljIGZsdXggZGF0YSBmcm9tIHRoZSBzdXBwbGVtZW50YWwgaXRlbXMgb2YgW05ha2FqaW1hIGV0IGFsLiwgMjAxNF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvcGNwL3BjdTA5MSkuCgpgYGB7cn0KbGlicmFyeShmbHVjdHVhdG9yKQoKIyBpbXBvcnQgZmx1eCBkYXRhCmRmX25ha2FqaW1hX21mYSA8LSByZWFkLmNzdigiLi4vZGF0YS9pbnB1dC9OYWthamltYTIwMTRfbWV0YWJvbGljX2ZsdXhlcy5jc3YiKQoKIyBnZW5lcmF0ZSBzdHJva2Ugd2lkdGggYW5kIGNvbG9yCmRmX25ha2FqaW1hX21mYSA8LSBkZl9uYWthamltYV9tZmEgJT4lCiAgbXV0YXRlKAogICAgc3Ryb2tlX3dpZHRoID0gMC4zICsgKDAuNypzcXJ0KGFicyhmbHV4KSkpLAogICAgc3Ryb2tlX2NvbG9yID0gYWJzKGZsdXgpICU+JSB7MSsoLi9tYXgoLikpKjl9ICU+JSByb3VuZCwKICAgIHN0cm9rZV9jb2xvcl9yZ2IgPSAgY29sb3JSYW1wUGFsZXR0ZShjdXN0b21fY29sb3JzW2MoNSwyLDEpXSkoMTApW3N0cm9rZV9jb2xvcl0pCmBgYAoKVGhlIG5leHQgc3RlcCBpcyB0byBvdmVybGF5IHRoZSBmbHV4ZXMuIFdlIGdlbmVyYXRlIHR3byB0eXBlcyBvZiBtYXBzLCBtaXhvdHJvcGh5IGFuZCBwaG90b2hldGVyb3Ryb3BoeS4KVGhlIHN0cm9rZSB3aWR0aCBhbmQgY29sb3IgZm9yIGFsbCByZWFjdGlvbnMgaXMgc2V0IGJ5IHRoZSBmbHV4IG1hZ25pdHVkZS4KCmBgYHtyfQpmb3IgKGNvbmQgaW4gYygibWl4b3Ryb3BoIiwgInBob3RvaGV0ZXJvdHJvcGgiKSkgewogICMgaW1wb3J0IG1hcCAKICBTVkdfdGVtcGxhdGUgPC0gcmVhZF9zdmcoIi4uL2RhdGEvaW5wdXQvbWFwX2NlbnRyYWxfbWV0YWJvbGlzbV9zeW4uc3ZnIikKICAKICAjIHNldCBzdHJva2Ugb24gU1ZHIG1hcAogIFNWR19taXggPC0gc2V0X2F0dHJpYnV0ZXMoU1ZHX3RlbXBsYXRlLAogICAgbm9kZSA9IGZpbHRlcihkZl9uYWthamltYV9tZmEsIGNvbmRpdGlvbiA9PSBjb25kKSRyZWFjdGlvbiwKICAgIGF0dHIgPSAic3R5bGUiLAogICAgcGF0dGVybiA9ICJzdHJva2Utd2lkdGg6WzAtOV0rXFwuWzAtOV0rIiwKICAgIHJlcGxhY2VtZW50ID0gcGFzdGUwKCJzdHJva2Utd2lkdGg6IiwKICAgICAgZmlsdGVyKGRmX25ha2FqaW1hX21mYSwgY29uZGl0aW9uID09IGNvbmQpJHN0cm9rZV93aWR0aCkpCiAgCiAgIyBzZXQgY29sb3IKICBTVkdfbWl4IDwtIHNldF9hdHRyaWJ1dGVzKFNWR19taXgsCiAgICBub2RlID0gZmlsdGVyKGRmX25ha2FqaW1hX21mYSwgY29uZGl0aW9uID09IGNvbmQpJHJlYWN0aW9uLAogICAgYXR0ciA9ICJzdHlsZSIsCiAgICBwYXR0ZXJuID0gInN0cm9rZTojYjNiM2IzIiwKICAgIHJlcGxhY2VtZW50ID0gcGFzdGUwKCJzdHJva2U6IiwKICAgICAgZmlsdGVyKGRmX25ha2FqaW1hX21mYSwgY29uZGl0aW9uID09IGNvbmQpJHN0cm9rZV9jb2xvcl9yZ2IpKQogIAogICMgc2V0IGFycm93IGRpcmVjdGlvbmFsaXR5CiAgU1ZHX21peCA8LSBzZXRfYXR0cmlidXRlcyhTVkdfbWl4LAogICAgbm9kZSA9IGZpbHRlcihkZl9uYWthamltYV9tZmEsIGNvbmRpdGlvbiA9PSBjb25kLCBmbHV4IDwgMCkkcmVhY3Rpb24sCiAgICBhdHRyID0gInN0eWxlIiwKICAgIHBhdHRlcm4gPSAibWFya2VyLWVuZDp1cmxcXCgjbWFya2VyWzAtOV0qXFwpOyIsCiAgICByZXBsYWNlbWVudCA9ICIiKQogIAogIFNWR19taXggPC0gc2V0X2F0dHJpYnV0ZXMoU1ZHX21peCwKICAgIG5vZGUgPSBmaWx0ZXIoZGZfbmFrYWppbWFfbWZhLCBjb25kaXRpb24gPT0gY29uZCwgZmx1eCA+IDApJHJlYWN0aW9uLAogICAgYXR0ciA9ICJzdHlsZSIsCiAgICBwYXR0ZXJuID0gIm1hcmtlci1zdGFydDp1cmxcXCgjbWFya2VyWzAtOV0qXFwpOyIsCiAgICByZXBsYWNlbWVudCA9ICIiKQogIAogIHdyaXRlX3N2ZyhTVkdfbWl4LCBmaWxlID0gcGFzdGUwKCIuLi9kYXRhL291dHB1dC9tYXBfIiwgY29uZCwgInkuc3ZnIikpCn0KYGBgCgpNZXRhYm9saWMgZmx1eCB3aXRoIG1peG90cm9waHkgfCAgTWV0YWJvbGljIGZsdXggd2l0aCBwaG90b2hldGVyb3Ryb3BoeQo6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06CiFbXSguLi9kYXRhL291dHB1dC9tYXBfbWl4b3Ryb3BoeS5zdmcpICB8ICAhW10oLi4vZGF0YS9vdXRwdXQvbWFwX3Bob3RvaGV0ZXJvdHJvcGh5LnN2ZykKCgpOb3cgd2UgcGxvdCBmaXRuZXNzIG9mIGNlbnRyYWwgY2FyYm9uIG1ldGFib2xpc20gZ2VuZXMgZm9yIHR3byBvciB0aHJlZSBzZWxlY3RlZCBjb25kaXRpb25zLiBUaGVzZSB3aWxsIGJlIGFkZGVkIHRvIHRoZSBtZXRhYm9saWMgbWFwIG1hbnVhbGx5LiBUaGUgbWl4b3Ryb3BoaWMgY29uZGl0aW9ucyBgTEMsIExMLCArR2AgYW5kIGBIQywgTEwsICtHYCB0dXJuZWQgb3V0IHRvIGJlIHZlcnkgc2ltaWxhci4KCmBgYHtyfQpkZl9jZW50cmFsY2FyYiA8LSB0aWJibGUoCiAgbG9jdXMgPSBjKCAgICJzbGwwNTkzIiwgInNscjAzMjkiLCAic2xyMTg0MyIsICJzbGwxNDc5IiwgInNsbDAzMjkiLCAic2xyMTM0OSIsCiAgICAic2xyMDk1MiIsICJzbHIyMDk0IiwgInNscjA5NDMiLCAic2xsMDAxOCIsICJzbHIwNzgzIiwgInNsbDEzNDIiLCAic2xyMDg4NCIsCiAgICAic2xyMDM5NCIsICJzbHIxOTQ1IiwgInNscjA3NTIiLCAic2xsMDU4NyIsICJzbGwxMjc1IiwgInNsbDEwNzAiLCAic2xyMTc5MyIsCiAgICAic2xyMDE5NCIsICJzc2wyMTUzIiwgInNsbDA4MDciLCAic2xsMTUyNSIsICJzbHIwMDA5IiwgInNscjAwMTIiLCAic2xsMTcyMSIsCiAgICAic2xsMTg0MSIsICJzbHIxMDk2IiwgInNscjE5MzQiLCAic2xsMDQwMSIsICJzbHIwNjY1IiwgInNscjEyODkiLCAic2xyMTA5NiIsCiAgICAic2xsMTAyMyIsICJzbGwxNTU3IiwgInNsbDA4MjMiLCAic2xsMTYyNSIsICJzbHIwMjAxIiwgInNscjEyMzMiLCAic2xyMDAxOCIsCiAgICAic2xsMDg5MSIsICJzbGwwOTIwIiwgInNscjA3MjEiKSwKICByZWFjdGlvbiA9IGMoIkhFWCIsICJIRVgiLCAiRzZQREgiLCAiUEdMIiwgIkdORCIsICJQR0kiLCAiRkJQIiwKICAgICJGQlAiLCAiRkJBIiwgIkZCQSIsICJUUEkiLCAiR0FQREgiLCAiR0FQREgiLCAiUEdLIiwgIlBHTSIsICJFTk8iLAogICAgIlBZSyIsICJQWUsiLCAiVEtUIiwgIlRBTCIsICJSUEkiLCAiUlBJIiwgIlJQRSIsICJQUlVLIiwgIlJVQklTQ08iLAogICAgIlJVQklTQ08iLCAiUERIIiwgIlBESCIsICJQREgiLCAiUERIIiwgIkNTIiwgIkFDT05UIiwgIklDREgiLCAiQUtHREgiLAogICAgIlNVQ09BUyIsICJTVUNPQVMiLCAiU1VDRCIsICJTVUNEIiwgIlNVQ0QiLCAiU1VDRCIsICJGVU0iLCAiTURIIiwKICAgICJQUEMiLCAiTUUiKSkgJT4lCiAgaW5uZXJfam9pbihkZl9rZWdnKSAlPiUgZ3JvdXBfYnkobG9jdXMpICU+JSBzbGljZSgxKSAlPiUKICB1bmdyb3VwICU+JSBhcnJhbmdlKHJlYWN0aW9uKQpgYGAKCgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA1fQpwbG90X2NlbnRyYWxjYXJiX21pbmlmaWcgPC0gZGZfZ2VuZSAlPiUgZmlsdGVyKAogICAgdGltZSA9PSAwLAogICAgY29uZGl0aW9uICVpbiUgYygiTEMsIExMIiwgIkxDLCBMTCwgK0ciLCAiTEMsIExMLCArRCwgK0ciKSkgJT4lCiAgaW5uZXJfam9pbihkZl9jZW50cmFsY2FyYiwgYnkgPSAibG9jdXMiKSAlPiUKICBtdXRhdGUoc2dSTkFfdGFyZ2V0ID0gcGFzdGUwKHJlYWN0aW9uLCAiICgiLCBzZ1JOQV90YXJnZXQsICIpIikpICU+JQogIG11dGF0ZShjb25kaXRpb24gPSBmYWN0b3IoY29uZGl0aW9uLCBjKCJMQywgTEwiLCAiTEMsIExMLCArRyIsICJMQywgTEwsICtELCArRyIpKSkgJT4lCiAgCiAgZ2dwbG90KGFlcyh4ID0gY29uZGl0aW9uLCB5ID0gd21lYW5fZml0bmVzcywgCiAgICB5bWluID0gd21lYW5fZml0bmVzcy1zZF9maXRuZXNzLCAKICAgIHltYXggPSB3bWVhbl9maXRuZXNzK3NkX2ZpdG5lc3MsIGZpbGwgPSBjb25kaXRpb24sIGNvbG9yID0gY29uZGl0aW9uKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoMCwgLTUsIC0xMCksIGxpbmV0eXBlID0gMywgY29sID0gZ3JleSgwLjYpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiLCB3aWR0aCA9IDAuNikgKwogIGdlb21fZXJyb3JiYXIocG9zaXRpb24gPSAiZG9kZ2UiLCB3aWR0aCA9IDAuNiwgc2l6ZSA9IDEpICsKICBjdXN0b21fdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNCwgImNtIikpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkgKwogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTExLCAxKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNbYyg1LDIsMyldKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNbYyg1LDIsMyldKSArCiAgZmFjZXRfd3JhcCh+IHNnUk5BX3RhcmdldCwgbmNvbCA9IDksIGRyb3AgPSBGQUxTRSkKCnBsb3RfY2VudHJhbGNhcmJfbWluaWZpZwpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL3Bsb3RfY2VudHJhbGNhcmJfbWluaWZpZy5zdmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUpCnBsb3RfY2VudHJhbGNhcmJfbWluaWZpZwpkZXYub2ZmKCkKYGBgCgpTaW1pbGFyIGJ1dCBtb3JlIGNvbmNpc2VseSwgd2UgY2FuIHRlc3QgaWYgdGhlcmUgaXMgYSBjb3JyZWFsdGlvbiBiZXR3ZWVuIGZsdXggYW5kIGZpdG5lc3MgcGVuYWx0eSB1cG9uIHJlcHJlc3Npb24uIFRoZW9yZXRpY2FsbHksIHN1Y2ggYSBjb3JyZWxhdGlvbiBzaG91bGQgZXhpc3QgYmVjYXVzZSByZXByZXNzaW9uIG9mIGhpZ2ggZmx1eCBlbnp5bWVzIHNob3VsZCBoYXZlIHRoZSBzdHJvbmdlc3QgcGVuYWx0eSBvbiBmaXRuZXNzLiBIb3dldmVyLCBleGFtaW5hdGlvbiBvZiB0aGUgZGF0YSBkb2VzIG5vdCByZXZlYWwgYSBjbGVhciBjb3JyZWxhdGlvbi4gQ2F1c2VzIGZvciB0aGlzIG1heSBiZSBtYW5pZm9sZCwgaW5jbHVkaW5nIHRoZSBjb21wZW5zYXRpb24gb2YgZ2VuZSBkdXBsaWNhdGVzL2lzby1lbnp5bWVzIGZvciBnZW5lIGtub2NrIGRvd24gYXQgaGlnaCBmbHV4IHJlYWN0aW9ucy4KCmBgYHtyfQpkZl9nZW5lICU+JSBmaWx0ZXIoCiAgICB0aW1lID09IDAsCiAgICBjb25kaXRpb24gJWluJSBjKCJMQywgTEwsICtHIiwgIkxDLCBMTCwgK0QsICtHIikpICU+JQogIG11dGF0ZShjb25kaXRpb24gPSByZWNvZGUoY29uZGl0aW9uLCAKICAgIGBMQywgTEwsICtHYCA9ICJtaXhvdHJvcGgiLCBgTEMsIExMLCArRCwgK0dgID0gInBob3RvaGV0ZXJvdHJvcGgiKSkgJT4lCiAgaW5uZXJfam9pbihkZl9jZW50cmFsY2FyYiwgYnkgPSAibG9jdXMiKSAlPiUKICBzZWxlY3Qoc2dSTkFfdGFyZ2V0LCBsb2N1cywgZ2VuZV9uYW1lLCBjb25kaXRpb24sIHdtZWFuX2ZpdG5lc3MsIHNkX2ZpdG5lc3MsIHJlYWN0aW9uKSAlPiUKICBpbm5lcl9qb2luKGJ5ID0gYygiY29uZGl0aW9uIiwgInJlYWN0aW9uIiksCiAgICBzZWxlY3QoZGZfbmFrYWppbWFfbWZhLCBjb25kaXRpb24sIHJlYWN0aW9uLCBmbHV4LCBjaV9sb3csIGNpX2hpZ2gpKSAlPiUKICAKICBnZ3Bsb3QoYWVzKHggPSBhYnMoZmx1eCksIHkgPSBhYnMod21lYW5fZml0bmVzcyksCiAgICB5bWluID0gYWJzKHdtZWFuX2ZpdG5lc3MpLXNkX2ZpdG5lc3MsIAogICAgeW1heCA9IGFicyh3bWVhbl9maXRuZXNzKStzZF9maXRuZXNzLAogICAgeG1pbiA9IGFicyhjaV9sb3cpLCAKICAgIHhtYXggPSBhYnMoY2lfaGlnaCkpKSArCiAgZ2VvbV9lcnJvcmJhcihvcmllbnRhdGlvbiA9ICJ4IiwgY29sID0gZ3JleSgwLjc1KSkgKwogIGdlb21fZXJyb3JiYXIob3JpZW50YXRpb24gPSAieSIsIGNvbCA9IGdyZXkoMC43NSkpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtMC4yLCAzLjIpLCB5bGltID0gYygtMC41LCA3LjUpKSArCiAgY3VzdG9tX3RoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsgCiAgZmFjZXRfd3JhcCh+IGNvbmRpdGlvbiwgbmNvbCA9IDIpCmBgYAoKCgojIyBBZGFwdGF0aW9uIHRvIGxpZ2h0IGFuZCBjYXJib24gZXhjZXNzCgpXZSB3aWxsIGxvb2sgYXQgdGhyZWUgZGlmZmVyZW50IHR5cGVzIG9mIHJlZ3VsYXRvcnkgYWRhcHRhdGlvbnM6CgotIGBhcGNgL2BjcGNgYW50ZW5uYSBwcm90ZWlucyAocGh5Y29iaWxpc29tZXMpLCBrbm93biB0byBiZSBhbW9uZyB0aGUgbW9zdCBleHByZXNzZWQgYW5kIHJlZ3VsYXRlZCBnZW5lcyBpbiBjeWFub3MKLSBmbGF2b3Byb3RlaW5zIEZsdjEgKGBzbGwxNTIxYCksIEZsdjIgKGBzbGwwMjE5YCksIEZsdjMgKGBzbGwwNTUwYCksIEZsdjQgKGBzbGwwMjE3YCksIGBzbGwwMjE4YCAoaW4gZmx2Mi80IG9wZXJvbikKLSBsb3cgYWZmaW5pdHkvaGlnaCBmbHV4IHRyYW5zcG9ydGVycyBDaSB0cmFuc3BvcnRlcnM6IGJpY0EgKGBzbGwwODM0YCksIE5ESC1JNCB3aXRoIG5kaEY0LCBENCwgY3VwQiAoYHNsbDAwMjZgLCBgc2xsMDAyN2AsIGBzbHIxMzAyYCkKLSBoaWdoIGFmZmluaXR5L2xvdyBmbHV4IGluZHVjaWJsZSBDaSB0cmFuc3BvcnRlcnM6IEJDVDEvY21wQUIocG9yQilDRCAoYHNscjAwNDAtNDRgKSwgU2J0QS9CIChgc2xyMTUxMmAsIGBzbHIxNTEzYCksIE5ESC1JMyB3aXRoIG5kaEYzLCBuZGhEMywgY3VwQSwgY3VwUyAoYHNsbDE3MzItMzVgKQotIGNhcmJvbiB0cmFuc3BvcnQgcmVndWxhdG9yeSBwcm90ZWluczogY2NtUi9yYmNSIChgc2xsMTU5NGApLCBjbXBSIChgc2xsMDAzMGApLCBjeWFickIxIChgc2xsMDM1OWApLCBjeWFickIyIChgc2xsMDgyMmApCgoKYGBge3J9CnBsb3RfcGh5Y29iaWxpc29tZSA8LSBkZl9nZW5lICU+JSBmaWx0ZXIoc3RyX2RldGVjdChnZW5lX25hbWUsICJbYWNdcGNbQUJDREVGR10iKSkgJT4lCiAgcGxvdF9nZW5lX2ZpdG5lc3MobmNvbCA9IDYsIGxlZ2VuZC5wb3NpdGlvbiA9IDApCgpwbG90X2Zsdl9nZW5lcyA8LSBkZl9nZW5lICU+JSBmaWx0ZXIobG9jdXMgJWluJSBjKCJzbGwxNTIxIiwgInNsbDAyMTkiLCAic2xsMDU1MCIsICJzbGwwMjE3IiwgInNsbDAyMTgiKSkgJT4lCiAgbXV0YXRlKHNnUk5BX3RhcmdldCA9IHJlY29kZShzZ1JOQV90YXJnZXQsIGBzbGwxNTIxYCA9ICJGbHYxIChzbGwxNTIxKSIsIGBzbGwwMjE5YCA9ICJGbHYyIChzbGwwMjE5KSIsCiAgICBgc2xsMDU1MGAgPSAiRmx2MyAoc2xsMDU1MCkiLCBgc2xsMDIxN2AgPSAiRmx2NCAoc2xsMDIxNykiKSkgJT4lCiAgbXV0YXRlKHNnUk5BX3RhcmdldCA9IGZhY3RvcihzZ1JOQV90YXJnZXQsIGModW5pcXVlKHNnUk5BX3RhcmdldCksICIiKSkpICU+JQogIHBsb3RfZ2VuZV9maXRuZXNzKG5jb2wgPSA2LCBsZWdlbmQucG9zaXRpb24gPSAwKQoKcGxvdF9jYXJib25fdXB0YWtlIDwtIGRmX2dlbmUgJT4lIGZpbHRlcihsb2N1cyAlaW4lIGMoCiAgICAic2xsMDAyNiIsICJzbGwwMDI3IiwgInNscjEzMDIiLAogICAgInNsbDE3MzIiLCAic2xsMTczMyIsICJzbGwxNzM0IiwgInNsbDE3MzUiLCAic2xyMDA0MCIsICJzbHIwMDQxIiwic2xyMDA0MyIsInNscjAwNDQiCiAgKSkgJT4lCiAgbXV0YXRlKHNnUk5BX3RhcmdldCA9IHJlY29kZShzZ1JOQV90YXJnZXQsCiAgICBgbnJ0QzJgID0gImNtcEMiLCBgbnJ0RDNgID0gImNtcEQiLAogICAgYHNsbDE3MzRgID0gImN1cEEiLCBgc2xyMTMwMmAgPSAiY3VwQiIsCiAgICBgc2xsMTczNWAgPSAiY3VwUyIsIGBuZGhGMmAgPSAibmRoRjMiCiAgKSkgJT4lCiAgbXV0YXRlKHNnUk5BX3RhcmdldCA9IGZhY3RvcihzZ1JOQV90YXJnZXQsIHVuaXF1ZShzZ1JOQV90YXJnZXQpW2MoNCw2LDExLDMsNSw5LDEwLDEsMiw3LDgpXSkpICU+JQogIHBsb3RfZ2VuZV9maXRuZXNzKG5jb2wgPSA2LCBsZWdlbmQucG9zaXRpb24gPSAwKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC03LjksIDIuNCkpCmBgYAoKRmlndXJlIDMgZHJhZnQ6CgpgYGB7ciwgZmlnLndpZHRoID0gNi4wLCBmaWcuaGVpZ2h0ID0gOC41fQpnZ2FycmFuZ2UobnJvdyA9IDMsIGhlaWdodHMgPSAgYygwLjQ3LCAwLjIsIDAuMzMpLCBsYWJlbHMgPSBMRVRURVJTWzE6M10sIGZvbnQubGFiZWwgPSBsaXN0X2ZvbnRwYXJzLAogIHBsb3RfcGh5Y29iaWxpc29tZSwKICBwbG90X2Zsdl9nZW5lcywKICBwbG90X2NhcmJvbl91cHRha2UKKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL2ZpZ3VyZTMuc3ZnIiwgd2lkdGggPSA2LjAsIGhlaWdodCA9IDguNSkKZ2dhcnJhbmdlKG5yb3cgPSAzLCBoZWlnaHRzID0gIGMoMC40NywgMC4yLCAwLjMzKSwgbGFiZWxzID0gTEVUVEVSU1sxOjNdLCBmb250LmxhYmVsID0gbGlzdF9mb250cGFycywKICBwbG90X3BoeWNvYmlsaXNvbWUsCiAgcGxvdF9mbHZfZ2VuZXMsCiAgcGxvdF9jYXJib25fdXB0YWtlCikKZGV2Lm9mZigpCmBgYAoKQXMgYSBTdXBwbGVtZW50YXJ5IGZpZ3VyZSB0byBDKSwgd2UgY2FuICoqcGxvdCBhbGwgb3RoZXIgY2FyYm9uIHRyYW5zcG9ydGVycyBhbmQgcmVndWxhdG9yeSBnZW5lcyoqIHRoYXQgc2hvd2VkIGEgbGVzcyByZW1hcmthYmxlIGVmZmVjdC4KCmBgYHtyLCBmaWcud2lkdGggPSA2LCAgZmlnLmhlaWdodCA9IDIuNzV9CnBsb3RfY2FyYm9uX3VwdGFrZV8yIDwtIGRmX2dlbmUgJT4lIGZpbHRlcihsb2N1cyAlaW4lIGMoCiAgICAic2xsMDgzNCIsICJzbHIxNTEyIiwgInNscjE1MTMiLCAic2xsMTU5NCIsICJzbGwwMDMwIiwgInNsbDAzNTkiLCAic2xsMDgyMiIKICApKSAlPiUKICBtdXRhdGUoc2dSTkFfdGFyZ2V0ID0gcmVjb2RlKHNnUk5BX3RhcmdldCwKICAgIGBzbGwwODM0YCA9ICJiaWNBIiwgYHNscjE1MTJgID0gInNidEEiLCBgc2xyMTUxM2AgPSAic2J0QiIsCiAgICBgc2xsMDM1OWAgPSAiY3lhYnJCMSIsIGBzbGwwODIyYCA9ICJjeWFickIyIiwgYHJiY1JgID0gImNjbVIiCiAgKSkgJT4lCiAgbXV0YXRlKHNnUk5BX3RhcmdldCA9IGZhY3RvcihzZ1JOQV90YXJnZXQsIHVuaXF1ZShzZ1JOQV90YXJnZXQpKSkgJT4lCiAgcGxvdF9nZW5lX2ZpdG5lc3MobmNvbCA9IDQsIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCgpwbG90X2NhcmJvbl91cHRha2VfMgpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL3Bsb3RfY2FyYm9uX3VwdGFrZS5zdmciLCB3aWR0aCA9IDYuMCwgaGVpZ2h0ID0gMi43NSkKcHJpbnQocGxvdF9jYXJib25fdXB0YWtlXzIpCmRldi5vZmYoKQpgYGAKCgpBcyBhbm90aGVyIFN1cHBsZW1lbnRhcnkgRmlndXJlLCB3ZSBjYW4gcGxvdCB0aGUgKip0b3RhbCBwcm90ZWluIG1hc3Mgb2YgdGhlIHBoeWNvYmlsaXNvbWUqKiBkZXRlcm1pbmVkIGJ5IHByb3RlaW4gbWFzcyBzcGVjdHJvbWV0cnkuClRoaXMgZGF0YSB3YXMgcHVibGlzaGVkIGluIG91ciBzdHVkeSBbSmFobiBldCBhbC4sIENlbGwgUmVwb3J0cywgMjAxOF0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1yZXBvcnRzL2Z1bGx0ZXh0L1MyMjExLTEyNDcoMTgpMzE0ODUtMikuIFRoZSBkYXRhIGNhbiBiZSBkb3dubG9hZGVkIGRpcmVjdGx5IGZyb20gdGhlIFNoaW55UHJvdCBnaXRodWIgcGFnZSB3aGVyZSBpdCBpcyBpbmNsdWRlZCBmb3Igb24gZGVtYW5kIHZpc3VhbGl6YXRpb24uCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDMuNn0KbG9hZCh1cmwoImh0dHBzOi8vZ2l0aHViLmNvbS9tLWphaG4vU2hpbnlQcm90L2Jsb2IvbWFzdGVyL2RhdGEvSmFobl8yMDE4X0xpZ2h0X2FuZF9DTzJfbGltLlJkYXRhP3Jhdz10cnVlIikpCgpwbG90X3Byb3RtYXNzX3BoeWNvYmlsaXNvbWUxIDwtIEphaG5fMjAxOF9MaWdodF9hbmRfQ08yX2xpbSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChwcm90ZWluLCAiW2FjXXBjW0FCQ0RFRkddIiksIHNhbXBsZSAhPSAiQ08yIikgJT4lCiAgbXV0YXRlKHByb3RlaW4gPSBzdHJfZXh0cmFjdChwcm90ZWluLCAiW2FjXXBjW0FCQ0RFRkddWzEyXT8iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGxpZ2h0KSwgeSA9IDEwMCptZWFuX21hc3NfZnJhY3Rpb25fbm9ybSwgCiAgZmlsbCA9IHN0cl9zdWIocHJvdGVpbiwgMSwgMyksIGxhYmVsID0gcHJvdGVpbikpICsKICBsaW1zKHkgPSBjKDAsIDIyKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgd2lkdGggPSAwLjcsIGNvbCA9IGdyZXkoMSksIHNpemUgPSAwLjIpICsKICBnZW9tX3RleHQoc2l6ZSA9IDIuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIndoaXRlIikgKwogIGN1c3RvbV90aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjUsICJjbSIpKSArCiAgbGFicyh0aXRsZSA9ICJMaWdodCBsaW1pdGF0aW9uIiwgeCA9ICLCtW1vbCBwaG90b25zIG1eLTIgc14tMSIsIHkgPSAiJSBwcm90ZWluIG1hc3MiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9yc1tjKDIsNCldKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNbYygyLDQpXSkKCnBsb3RfcHJvdG1hc3NfcGh5Y29iaWxpc29tZTIgPC0gSmFobl8yMDE4X0xpZ2h0X2FuZF9DTzJfbGltICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHByb3RlaW4sICJbYWNdcGNbQUJDREVGR10iKSwgc2FtcGxlID09ICJDTzIiKSAlPiUKICBtdXRhdGUocHJvdGVpbiA9IHN0cl9leHRyYWN0KHByb3RlaW4sICJbYWNdcGNbQUJDREVGR11bMTJdPyIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoY28yX2NvbmNlbnRyYXRpb24pLCB5ID0gMTAwKm1lYW5fbWFzc19mcmFjdGlvbl9ub3JtLCAKICBmaWxsID0gc3RyX3N1Yihwcm90ZWluLCAxLCAzKSwgbGFiZWwgPSBwcm90ZWluKSkgKwogIGxpbXMoeSA9IGMoMCwgMjIpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAic3RhY2siLCB3aWR0aCA9IDAuNywgY29sID0gZ3JleSgxKSwgc2l6ZSA9IDAuMikgKwogIGdlb21fdGV4dChzaXplID0gMi41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgY29sb3IgPSAid2hpdGUiKSArCiAgY3VzdG9tX3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwgImNtIikpICsKICBsYWJzKHRpdGxlID0gIkNPMiBsaW1pdGF0aW9uIiwgeCA9ICIlIENPMiBpbiBhaXIiLCB5ID0gIiUgcHJvdGVpbiBtYXNzIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNbYygyLDQpXSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzW2MoMiw0KV0pCgpnZ2FycmFuZ2UobmNvbCA9IDIsIHdpZHRocyA9IGMoMC41LDAuNSksCiAgbGFiZWxzID0gTEVUVEVSU1sxOjJdLCBmb250LmxhYmVsID0gbGlzdF9mb250cGFycywKICBwbG90X3Byb3RtYXNzX3BoeWNvYmlsaXNvbWUxLAogIHBsb3RfcHJvdG1hc3NfcGh5Y29iaWxpc29tZTIKKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9wbG90X3Byb3RtYXNzX3BoeWNvYmlsaXNvbWUuc3ZnIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAzLjYpCmdnYXJyYW5nZShuY29sID0gMiwgd2lkdGhzID0gYygwLjUsMC41KSwKICBsYWJlbHMgPSBMRVRURVJTWzE6Ml0sIGZvbnQubGFiZWwgPSBsaXN0X2ZvbnRwYXJzLAogIHBsb3RfcHJvdG1hc3NfcGh5Y29iaWxpc29tZTEsCiAgcGxvdF9wcm90bWFzc19waHljb2JpbGlzb21lMgopCmRldi5vZmYoKQpgYGAKCk90aGVyIGdlbmVzIG9mIGludGVyZXN0IHRoYXQgZWl0aGVyIGRpZCBub3Qgc2hvdyBhbnkgKHJlbWFya2FibGUpIGVmZmVjdCBvbiBmaXRuZXNzLCBvciBkbyBub3QgbWVldCB0aGUgc2NvcGUgb2YgdGhpcyBzZWN0aW9uOgoKLSBPQ1AgKGBzbHIxOTYzYCksIHBncjUgKGBzc3IyMDE2YCkKLSBTaWdCIChgc2xsMDMwNmApLCBTaWdDIChgc2xsMDE4NGApLCBTaWdEIChgc2xsMjAxMmApLCBTaWdFIChgc2xsMTY4OWApIChycG9EIGdlbmVzIDEtNCkKLSBjY21NIChgc2xsMTAzMWApLCBjY21LMiAoYHNsbDEwMjhgKSwgY2NtSzEgKGBzbGwxMDI5YCksIGNjbU4gKGBzbGwxMDMyYCksIGNjbU8gKGBzbHIwNDM2YCksCiAgY2NtTCAoYHNsbDEwMzBgKQotIENQMTIgKGBzc2wzMzY0YCkKCgojIyBHZW5lcyB3aGVyZSBrbm9jayBkb3duIGxlYWRzIHRvIGluY3JlYXNlZCBmaXRuZXNzCgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4fQpsaXN0X2dlbmVzX3Bvc19maXRuZXNzIDwtIGRmX2dlbmUgJT4lCiAgZmlsdGVyKHRpbWUgPT0gMCwgIWlzLm5hKGxvY3VzKSwgd21lYW5fZml0bmVzcyA+IDIpICU+JQogIHB1bGwobG9jdXMpICU+JSB1bmlxdWUKCnBsb3RfZ2VuZV9maXRuZXNzKGRmX2dlbmUsIGdlbmUgPSBsaXN0X2dlbmVzX3Bvc19maXRuZXNzLCB0aXRsZSA9ICJHZW5lcyB3aXRoIGluY3JlYXNlZCBmaXRuZXNzIChmID4gMikiKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcGxvdF9maXRuZXNzX2luY3JlYXNlZC5zdmciLAogIHBsb3RfZ2VuZV9maXRuZXNzKGRmX2dlbmUsIGdlbmUgPSBsaXN0X2dlbmVzX3Bvc19maXRuZXNzLCB0aXRsZSA9ICJHZW5lcyB3aXRoIGluY3JlYXNlZCBmaXRuZXNzIChmID4gMikiKSwKICB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCmBgYAoKU3VtbWFyeToKLSBwbWdBIGlzIG9uY2UgYWdhaW4gdGhlIGdlbmUgd2l0aCBzdHJvbmdlc3QgYW5kIG1vc3Qgd2lkZXNwcmVhZCBmaXRuZXNzIGluY3JlYXNlLCB2YWxpZGF0aW5nIHJlc3VsdHMgZnJvbSBsaWJyYXJ5IFYxCi0gc2xyMTkxNiBzYW1lIHBoZW5vdHlwZSBhcyBwbWdBIGp1c3Qgd2Vha2VyLiBXZSBhbHNvIGtub3cgdGhpcyBvbmUgZnJvbSBiZWZvcmUuIE11c3QgaGF2ZSBpZGVudGljYWwgcm9sZSBhcyBwbWdBLgotIGFsbCBQU0lJIGdlbmVzIHNob3cgaW5jcmVhc2VkIGZpdG5lc3MgaW4gcGhvdG9oZXRlcm90cm9waGljIGNvbmRpdGlvbiAtLT4gUFMgaXMgYSBidXJkZW4gaGVyZQotIHNsbDA2ODksIHB4Y0EsIHNscjE2MDkgLSBhbGwgaW5jcmVhc2VkIGZpdG5lc3MgaW4gSEMsSEwsIGZpcnN0IHR3byBhcmUgTmErL0NPMiAoPykgdHJuYXNwb3J0ZXJzLAogIHNscjE2MDkgd2Uga25vdyBmcm9tIGJlZm9yZSwgICBhbm5vdGF0ZWQgYXMgZmF0dHkgYWNpZCBDb0EgbGlnYXNlLCBidXQgcHJvYmFibHkgaXQncyBzb21ldGhpbmcgZGlmZmVyZW50Ci0gc2xsNjA1NSwgc2xyMTUwNSwgc2xyMTk5MCAtIGFsbCBpbmNyZWFzZWQgZml0bmVzcyBpbiBwaG90b2hldGVyb3Ryb3BoaWMgY29uZGl0aW9uLCBhbmQgZGVjcmVhc2VkIGZpdG5lc3MgaW4gSEMvTEwgY29uZGl0aW9ucy4KICBOb3QgbXVjaCBpcyBrbm93biBhYm91dCB0aGVzZSBnZW5lcywgcHJvYmFibHkgYSByb2xlIGluIHBob3Rvc3ludGhlc2lzLCBhcyB0aGUgcGF0dGVybiBpcyBzaW1pbGFyIHRvIHBzYiBnZW5lcyAoUFNJSSBtYXR1cmF0aW9uPykKLSBzbHIwODEzLCBzbHIwOTA3LCBzbHI5MDksIHNscjEyOTkgLSBhbGwgaW5jcmVhc2VkIGZpdG5lc3MgaW4gSEMvTEwuIE5vdCBjbGVhciB3aGF0IGNvbm5lY3RzIHRoZXNlIGdlbmVzIGZ1bmN0aW9uYWxseS4KCgojIERpZmZlcmVudGlhbCBmaXRuZXNzIG9mIG5vbi1jb2RpbmcgUk5BcyAobmNSTkFzKQoKIyMgR2VuZXJhbCB0cmVuZHMKClRoZSBmaXJzdCB0YXNrIHRvIHN0dWR5IG5jUk5BcyBpcyB0byBnZW5lcmF0ZSBhIG5ldyBkYXRhIGZyYW1lIHdpdGggYWRkaXRpb25hbCBhbm5vdGF0aW9uIGZvciBuY1JOQXMuCkFkZGl0aW9uYWwgYW5ub3RhdGlvbiB0YWJsZXMgd2VyZSBleHBvcnRlZCBmcm9tIEdlbmVpb3VzIGFuZCBhcmUgYmFzZWQgb24gdGhlIHB1YmxpY2F0aW9uIGZyb20gW01pdHNjaGtlIGV0IGFsLiwgUE5BUywgMjAxMF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwNzMvcG5hcy4xMDE1MTU0MTA4KS4gQWNjb3JkaW5nIHRvIHRoaXMgcHVibGljYXRpb24sIG5jUk5BcyBhcmUgZ3JvdXBlZCBpbnRvIGZvdXIgZGlmZmVyZW50IChzbGlnaHRseSBvdmVybGFwcGluZykgY2xhc3NlczoKCi0gbm9uLWNvZGluZyByZWd1bGF0b3J5IFJOQXMgKG5jUk5BcyBpbiBzdHJpY3Qgc2Vuc2UpIG5vdCBhc3NvY2lhdGVkIHRvIGEgZ2VuZQotIGlUU1MsIGludGVybmFsIFRTUyB3aXRoaW4gYSBnZW5lCi0gYXNSTkEsIHJlZ3VsYXRvcnkgYW50aS1zZW5zZSBSTkFzIGFzc29jaWF0ZWQgd2l0aCBhIGdlbmUKLSBOb3QgaW5jbHVkZWQ6IDUnVVRScywgYWx0ZXJuYXRpdmUgdHJhbnNjcmlwdGlvbiBzdGFydCBzaXRlcyAoVFNTKSBhc3NvY2lhdGVkIHRvIGEgZ2VuZQoKYGBge3J9CmRmX25jUk5BIDwtIGRmX21haW4gJT4lIGZpbHRlcihzZ1JOQV90eXBlID09ICJuY1JOQSIpICU+JQogICMgb2J0YWluIG51bWJlciBvZiBzZ1JOQXMgcGVyIHRhcmdldAogIGdyb3VwX2J5KHNnUk5BX3RhcmdldCkgJT4lCiAgc3VtbWFyaXplKHNnUk5BX251bWJlciA9IGxlbmd0aCh1bmlxdWUoc2dSTkFfcG9zaXRpb24pKSkgJT4lCiAgIyBtZXJnZSB3aXRoIGRmX2dlbmUgdGFibGUKICBpbm5lcl9qb2luKGRmX2dlbmUsIGJ5ID0gInNnUk5BX3RhcmdldCIpICU+JQogICMgZ2VuZXJhdGUgbmNSTkEgdHlwZSBiYXNlZCBvbiB0YXJnZXQgbmFtZQogIHNlbGVjdCgtbG9jdXMsIC1nZW5lX25hbWUsIC1zZ1JOQV90eXBlKSAlPiUKICBtdXRhdGUoc2dSTkFfdGFyZ2V0ID0gc3RyX3N1YihzZ1JOQV90YXJnZXQsIDQsIDEwMDApKSAlPiUKICBsZWZ0X2pvaW4oYnkgPSAic2dSTkFfdGFyZ2V0IiwKICAgIGJpbmRfcm93cyhsYXBwbHkoYygiTkNfMDAwOTExX25jUk5BLnRzdiIsICJOQ18wMDA5MTFfYXNSTkEudHN2IiwgIk5DXzAwMDkxMV9pVFNTLnRzdiIpLAogICAgICBGVU4gPSBmdW5jdGlvbihmKSByZWFkX3RzdihwYXN0ZTAoIi4uL2RhdGEvaW5wdXQvIiwgZiksIGNvbF90eXBlcyA9IGNvbHMoKSkKICAgICkpCiAgKQoKZGZfbmNSTkEgJT4lIGdyb3VwX2J5KG5jUk5BX3R5cGUpICU+JQogIGZpbHRlcih0aW1lID09IDAsIGNvbmRpdGlvbiA9PSAiSEMsIEhMIikgJT4lCiAgc2VsZWN0KHNnUk5BX3RhcmdldCwgc2NvcmUpICU+JQogIHN1bW1hcml6ZShuX3RhcmdldHMgPSBsZW5ndGgodW5pcXVlKHNnUk5BX3RhcmdldCkpLCBzdW0oc2NvcmUgPj0gNCksIHN1bShzY29yZSA8IDQpLAogICAgcGVyY2VudCA9IHN1bShzY29yZSA+PSA0KS9uKCkqMTAwKQpgYGAKCkxvb2tpbmcgYXQgdGhlIGZpdG5lc3MgYW5kIHNpZ25pZmljYW5jZSBzY29yZXMgZm9yIG9uZSBjb25kaXRpb25zLCBpdCBzZWVtcyBhcyBpbnRlcm5hbCB0cmFuc2NyaXB0aW9uIHN0YXJ0IHNpdGVzIGFyZSBvdmVycmVwcmVzZW50ZWQgaW4gdGhlIGdyb3VwIHRoYXQgc2hvd3MgYW4gZWZmZWN0LiBUaGlzIGlzIG5vdCBhIHN1cnByaXNlLCBnaXZlbiB0aGF0IHNnUk5BcyB0YXJnZXRpbmcgaVRTUyBiYXNpY2FsbHkgYWxzbyByZXByZXNzIHRoZSBuYXRpdmUgZ2VuZSBhcyBhIHJlZ3VsYXIgc2dSTkEuIFdlIHRoZXJlZm9yZSBleGxjdWRlIGlUU1MgZnJvbSB0aGUgYW5hbHlzaXMuCgpUaGUgbGlicmFyeSBjb250YWlucyAxNzEyIG5jUk5BcyBlYWNoIHRhcmdldGVkIGJ5IDEgdG8gNSBzZ1JOQXMuIE9ubHkgdmVyeSBmZXcgb2YgdGhvc2Ugc2hvd2VkIGFuIGVmZmVjdCBvbiBmaXRuZXNzLgpXZSBjYW4gZmlsdGVyIGFsbCBuY1JOQXMgdGhhdCBoYXZlIGEgInNpZ25pZmljYW5jZSIgZXF1aXZhbGVudCB0byBhIGZpdG5lc3Mgc2NvcmUgYWJzKEYpID49IDIgYW5kIC1sb2cxMCBwLXZhbHVlID49IDIgKGFscGhhID0gMC4wMSkuClNpZ25pZmljYW5jZSBoZXJlIG1lYW5zIGVmZmVjdCBzaXplIChGKSBtdWx0aXBsaWVkIGJ5IC1sb2cxMCBwLXZhbHVlLCB0aGUgdGhyZXNob2xkIGlzIGluZGljYXRlZCBieSB0aGUgZGFzaGVkIGxpbmUuCgpgYGB7ciwgZmlnLndpZHRoID0gMi41LCBmaWcuaGVpZ2h0ID0gNn0KcGxvdF9uY1JOQV9vdmVydmlldyA8LSBkZl9uY1JOQSAlPiUgZmlsdGVyKHRpbWUgPT0gMCwgY29uZGl0aW9uID09ICJIQywgSEwiKSAlPiUKICBtdXRhdGUobmNSTkFfdHlwZSA9IGZhY3RvcihuY1JOQV90eXBlLCBjKCJhc1JOQSIsICJuY1JOQSIsICJpVFNTIikpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB3bWVhbl9maXRuZXNzLCB5ID0gLWxvZzEwKHBfdmFsdWVfYWRqKSwgY29sb3IgPSBuY1JOQV90eXBlKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAxLjUpICsKICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoc2VxKC04LCAtMC41LCAwLjEpLCBzZXEoMC41LCA4LCAwLjEpKSwKICAgIHkgPSA0L2Moc2VxKDgsIDAuNSwgLTAuMSksIHNlcSgwLjUsIDgsIDAuMSkpKSwKICAgIGFlcyh4ID0geCwgeSA9IHksIHNoYXBlID0gTlVMTCwgY29sID0gTlVMTCksIGx0eSA9IDIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTcsIDcpLCB5bGltID0gYygwLCA0KSkgKwogIGN1c3RvbV90aGVtZShhc3BlY3QgPSAxLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBmYWNldF93cmFwKH4gbmNSTkFfdHlwZSwgbmNvbCA9IDEpICsKICBsYWJzKHggPSAiZml0bmVzcyIsIHkgPSBleHByZXNzaW9uKCItbG9nIlsxMF0qIiBwLXZhbHVlIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9ycykKCnByaW50KHBsb3RfbmNSTkFfb3ZlcnZpZXcpCmBgYAojIyBBbnRpc2Vuc2UgUk5BcyBhcyByZWd1bGF0b3J5IGVsZW1lbnRzCgpUaGUgZmlyc3QgcGFydCBvZiBhIG1vcmUgZGV0YWlsZWQgYW5hbHlzaXMgaXMgdG8gZXh0cmFjdCBhc1JOQXMgd2l0aCBkaWZmZXJlbnRpYWwgZml0bmVzcywgYW5kIGNvbXBhcmUgdGhlbSB0byB0aGVpciBhc3NvY2lhdGVkIGdlbmVzLiBUaGUgYXNzdW1wdGlvbiBpcyB0aGF0IHNnUk5BcyB0YXJnZXRpbmcgYXNSTkFzIGluIHJlYWxpdHkgcmVwcmVzcyB0cmFuc2NyaXB0aW9uIG9mIHRoZWlyIHBhcmVudCBnZW5lcywgYW5kIGJ5IHRoZXNlIG1lYW5zIHByb2R1Y2UgYSBmaXRuZXNzIGVmZmVjdCB0aGF0IGNhbiBub3QgYmUgYXR0cmlidXRlZCB0byB0aGUgYWN0aW9uIG9mIHRoZSBhc1JOQSBpdHNlbGYuIFRoZSBmaXJzdCBzdGVwIGlzIGZpbHRlciB0aGUgbmNSTkEgZGF0YXNldCBhbmQgb3JkZXIgbmNSTkFzIGJ5IGZpdG5lc3Mgc2ltaWxhcml0eS4KCmBgYHtyLCBmaWcud2lkdGggPSAzLjUsIGZpZy5oZWlnaHQgPSA4fQpkZl9uY1JOQV9zZWxlY3QgPC0gZGZfbmNSTkEgJT4lCiAgZmlsdGVyKG5jUk5BX3R5cGUgIT0gImlUU1MiLCB0aW1lID09IDApICU+JQogIGdyb3VwX2J5KHNnUk5BX3RhcmdldCkgJT4lCiAgZmlsdGVyKGFueShzY29yZSA+PSA0KSkgJT4lIHVuZ3JvdXAgJT4lCiAgbXV0YXRlKHNnUk5BX3RhcmdldCA9IGZjdF9jbHVzdGVyKHNnUk5BX3RhcmdldCwgY29uZGl0aW9uLCB3bWVhbl9maXRuZXNzKSkKYGBgCgoKYGBge3J9CnBsb3RfYXNSTkFfaGVhdCA8LSBkZl9uY1JOQV9zZWxlY3QgJT4lIGZpbHRlcihuY1JOQV90eXBlID09ICJhc1JOQSIpICU+JQogIG11dGF0ZSh3bWVhbl9maXRuZXNzID0gd21lYW5fZml0bmVzcyAlPiUgcmVwbGFjZSguLCAuID4gNCwgNCkgJT4lIHJlcGxhY2UoLiwgLiA8IC00LCAtNCkpICU+JQogIGdncGxvdChhZXMoeCA9IGNvbmRpdGlvbiwgeSA9IHNnUk5BX3RhcmdldCwgZmlsbCA9IHdtZWFuX2ZpdG5lc3MpKSArCiAgZ2VvbV90aWxlKCkgKyBjdXN0b21fdGhlbWUobGVnZW5kLnBvcyA9ICJib3R0b20iKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoY3VzdG9tX2NvbG9yc1sxXSwgZ3JleSgwLjkpLCBjdXN0b21fY29sb3JzWzJdKSwKICAgIGxpbWl0cyA9IGMoLTQsIDQpKQoKIyBjaGVjayBjb3JyZWxhdGlvbiBvZiBhc1JOQSBmaXRuZXNzIHdpdGggYXNzb2NpYXRlZCBnZW5lIGZpdG5lc3MKcGxvdF9hc1JOQV94eSA8LSBkZl9uY1JOQV9zZWxlY3QgJT4lIGZpbHRlcihuY1JOQV90eXBlID09ICJhc1JOQSIpICU+JQogIGxlZnRfam9pbihieSA9IGMoImNvbmRpdGlvbiIsICJsb2N1cyIpLAogICAgc2VsZWN0KGRmX2dlbmUsIGxvY3VzLCBjb25kaXRpb24sIHdtZWFuX2ZpdG5lc3MsIHNkX2ZpdG5lc3MpICU+JSBkaXN0aW5jdCAlPiUKICAgIHJlbmFtZShnZW5lX2ZpdG5lc3MgPSB3bWVhbl9maXRuZXNzLCBzZF9nZW5lX2ZpdG5lc3MgPSBzZF9maXRuZXNzKSkgJT4lCiAgc2VsZWN0KGxvY3VzLCBjb25kaXRpb24sIHdtZWFuX2ZpdG5lc3MsIGdlbmVfZml0bmVzcykgJT4lCiAgbXV0YXRlKGxvY3VzID0gaWZfZWxzZShsb2N1cyAlaW4lIGMoInNscjA4ODIiLCAic2xsMTc3MyIsICJzbWwwMDA0IiwgInNscjE2MDkiLCAic2xyMTkzOSIpLCBsb2N1cywgIm90aGVyIikpICU+JQogIGdncGxvdChhZXMoeCA9IHdtZWFuX2ZpdG5lc3MsIHkgPSBnZW5lX2ZpdG5lc3MsIGNvbG9yID0gbG9jdXMpKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsdHkgPSAyKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gNCwgc2xvcGUgPSAxLCBsdHkgPSAyKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gLTQsIHNsb3BlID0gMSwgbHR5ID0gMikgKwogIGdlb21fcG9pbnQoKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC05LCA1KSwgeWxpbSA9IGMoLTksIDUpKSArCiAgY3VzdG9tX3RoZW1lKGxlZ2VuZC5wb3MgPSBjKDAuMTUsIDAuOCksIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC4zLCAiY20iKSkgKwogIGxhYnMoeCA9ICJhc1JOQSBmaXRuZXNzIiwgeSA9ICJnZW5lIGZpdG5lc3MiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNbYyg1LDE6NCw2KV0pCmBgYAoKIyMgbm9uY29kaW5nIFJOQXMgYXMgcmVndWxhdG9yeSBlbGVtZW50cwoKVGhlIHNlY29uZCBwYXJ0IG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gbG9vayBhdCBub24tZ2VuZSBhc3NvY2lhdGVkIChpbnRlcmdlbmljKSBuY1JOQSBlbGVtZW50cy4gT2YgdGhlc2UsIHNldmVyYWwgYXJlIGtub3duIHRvIGhhdmUgYSByZWd1bGF0b3J5IGVmZmVjdC4KCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNy41fQpwbG90X25jUk5BX2hlYXQgPC0gZGZfbmNSTkFfc2VsZWN0ICU+JSBmaWx0ZXIobmNSTkFfdHlwZSA9PSAibmNSTkEiKSAlPiUKICBtdXRhdGUod21lYW5fZml0bmVzcyA9IHdtZWFuX2ZpdG5lc3MgJT4lIHJlcGxhY2UoLiwgLiA+IDQsIDQpICU+JSByZXBsYWNlKC4sIC4gPCAtNCwgLTQpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBzZ1JOQV90YXJnZXQsIGZpbGwgPSB3bWVhbl9maXRuZXNzKSkgKwogIGdlb21fdGlsZSgpICsgY3VzdG9tX3RoZW1lKGxlZ2VuZC5wb3MgPSAibm9uZSIpICsKICBsYWJzKHggPSAiIiwgeSA9ICIiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gYyhjdXN0b21fY29sb3JzWzFdLCBncmV5KDAuOSksIGN1c3RvbV9jb2xvcnNbMl0pLAogICAgbGltaXRzID0gYygtNCwgNCkpCgpnZ2FycmFuZ2UobmNvbCA9IDMsCiAgcGxvdF9uY1JOQV9vdmVydmlldywKICBnZ2FycmFuZ2UobnJvdyA9IDIsIGhlaWdodHMgPSBjKDAuNjUsIDAuMzUpLAogICAgcGxvdF9hc1JOQV9oZWF0LCBwbG90X2FzUk5BX3h5ICsgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoNCwxMiwxNiwxMiksICJwb2ludHMiKSkpLAogIGdnYXJyYW5nZShucm93ID0gMiwgaGVpZ2h0cyA9IGMoMC43NSwgMC4yNSksIHBsb3RfbmNSTkFfaGVhdCwgZ2dwbG90KCkgKyBjdXN0b21fdGhlbWUoKSkKKQpgYGAKCiMgRXhwb3J0IHN1bW1hcnkgdGFibGUgb2YgYWxsIGdlbmVzIGFuZCBjb25kaXRpb25zCgpFeHBvcnQgYSBzdW1tYXJ5IHRhYmxlIG9mIGFsbCBnZW5lcyBhbmQgY29uZGl0aW9ucywgc28gdGhhdCBpdCdzIGVhc3kgZm9yIG90aGVyIHBlb3BsZSB0byBsb29rIHVwIHNpbmdsZSBjb25kaXRpb25zIGFzIGZvciBleGFtcGxlIGRvbmUgaW4gW29uZS1ieS1vbmUgZml0bmVzcyBjb21wYXJpc29uc10oI2ZpdG5lc3Mtb2YtYWxsLWNvbmRpdGlvbnMtdnMtZWFjaC1vdGhlcikuIFRoaXMgaXMgYmVzdCBkb25lIGluIHdpZGUgZm9ybWF0IChvbmUgY29sdW1uIHBlciBjb25kaXRpb24pLgoKYGBge3J9CmRmX2dlbmUgJT4lIHVuZ3JvdXAgJT4lCiAgZmlsdGVyKHNnUk5BX3R5cGUgPT0gImdlbmUiKSAlPiUKICBzZWxlY3QobG9jdXMsIHNnUk5BX3RhcmdldCwgZ2VuZV9uYW1lLCBjb25kaXRpb24sIHdtZWFuX2ZpdG5lc3MpICU+JSAKICBkaXN0aW5jdCAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29uZGl0aW9uLCB2YWx1ZXNfZnJvbSA9IHdtZWFuX2ZpdG5lc3MpICU+JQogIHdyaXRlX2NzdigiLi4vZGF0YS9vdXRwdXQvZml0bmVzc19zdW1tYXJ5LmNzdiIpCgpkZl9nZW5lICU+JQogIGZpbHRlcihzZ1JOQV90eXBlID09ICJnZW5lIikgJT4lCiAgd3JpdGVfY3N2KCIuLi9kYXRhL291dHB1dC9maXRuZXNzX2dlbmVzLmNzdiIpCgpkZl9rZWdnICU+JSB3cml0ZV9jc3YoIi4uL2RhdGEvb3V0cHV0L2tlZ2dfYW5ub3RhdGlvbi5jc3YiKQpgYGAKClRoZSBlbnRpcmUgcGlwZWxpbmUgdGFrZXMgYWJvdXQgMjUgbWludXRlcyB0byBydW4gb24gYSBzdGFuZGFyZCBub3RlYm9vay4KVG8gd29yayBvbiBzaW5nbGUgc2VjdGlvbnMsIHRoZSB3b3JrIHNwYWNlIGlzIGV4cG9ydGVkIHRvIGF2b2lkIGNvbnN0YW50IHJlY2FsY3VsYXRpb24gb2YgcmVzdWx0IHRhYmxlcy4KCmBgYHtyfQojIHJlbW92ZSBsYXJnZSBpbnRlcm1lZGlhdGUgb2JqZWN0cwpybSgibGlzdF9jb25kaXRpb25fcGFpcnMiKQpzYXZlKGxpc3QgPSBscygpLCBmaWxlID0gIi4uL3BpcGVsaW5lL0NSSVNQUmlfVjJfZGF0YV9wcm9jZXNzaW5nLlJEYXRhIikKYGBgCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoK